Merge phase 29 country.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -43,12 +43,12 @@ const ru = {
|
||||
errorMissingMod: 'Не выбран обязательный мод «{id}».',
|
||||
errorModCycle: 'Выбранные моды зависят друг от друга по кругу.',
|
||||
errorInvalidCatalog: 'Не удалось загрузить выбранные моды.',
|
||||
errorUnknownNameSet: 'Выбранный набор имён не найден.',
|
||||
errorUnknownNativeLanguage: 'Выбранный родной язык не входит в этот набор имён.',
|
||||
errorUnknownCountry: 'Выбранная страна не найдена.',
|
||||
errorUnknownNativeLanguage: 'Выбранный родной язык не входит в эту страну.',
|
||||
catalogLoadFailed: 'Не удалось загрузить каталог модов.',
|
||||
|
||||
modsTitle: 'Моды',
|
||||
nameSetTitle: 'Набор имён',
|
||||
countryTitle: 'Страна',
|
||||
nativeLanguageTitle: 'Родной язык',
|
||||
nativeLanguageRandom: 'Случайный',
|
||||
randomNativeTitle: 'Выбрать язык наугад',
|
||||
@@ -260,12 +260,12 @@ const en: Messages = {
|
||||
errorMissingMod: 'Required mod “{id}” is not selected.',
|
||||
errorModCycle: 'The selected mods depend on each other in a cycle.',
|
||||
errorInvalidCatalog: 'The selected packs could not be loaded.',
|
||||
errorUnknownNameSet: 'The selected name set is not in the catalog.',
|
||||
errorUnknownNativeLanguage: 'The selected native language is not in that name set.',
|
||||
errorUnknownCountry: 'The selected country is not in the catalog.',
|
||||
errorUnknownNativeLanguage: 'The selected native language is not in that country.',
|
||||
catalogLoadFailed: 'Could not load the mod catalog.',
|
||||
|
||||
modsTitle: 'Mods',
|
||||
nameSetTitle: 'Name set',
|
||||
countryTitle: 'Country',
|
||||
nativeLanguageTitle: 'Native language',
|
||||
nativeLanguageRandom: 'Random',
|
||||
randomNativeTitle: 'Pick a language at random',
|
||||
|
||||
@@ -53,7 +53,7 @@ export async function fetchRandomName(lang: string): Promise<string> {
|
||||
export interface CreateSchoolOptions {
|
||||
readonly modIds?: readonly string[];
|
||||
readonly map?: MapLayout;
|
||||
readonly nameSetId?: string;
|
||||
readonly countryId?: string;
|
||||
readonly nativeLanguage?: string;
|
||||
readonly seed?: number;
|
||||
}
|
||||
@@ -71,7 +71,7 @@ export async function createSchool(
|
||||
startDate: startDate.toISOString(),
|
||||
modIds: extras.modIds ?? [],
|
||||
map: extras.map ?? null,
|
||||
nameSetId: extras.nameSetId ?? null,
|
||||
countryId: extras.countryId ?? null,
|
||||
nativeLanguage: extras.nativeLanguage ?? null,
|
||||
seed: extras.seed ?? null,
|
||||
}),
|
||||
@@ -92,10 +92,11 @@ export interface DefInfo {
|
||||
readonly pupilSlots?: number;
|
||||
}
|
||||
|
||||
export interface NameSetInfo {
|
||||
export interface CountryInfo {
|
||||
readonly defName: string;
|
||||
readonly label: string;
|
||||
readonly nativeLanguages: readonly DefInfo[];
|
||||
readonly climatePresets: readonly string[];
|
||||
}
|
||||
|
||||
export interface RoomSlotInfo {
|
||||
@@ -170,7 +171,7 @@ export interface CatalogResponse {
|
||||
readonly rooms: readonly RoomInfo[];
|
||||
readonly things: readonly DefInfo[];
|
||||
readonly defaultMap: MapLayout;
|
||||
readonly nameSets: readonly NameSetInfo[];
|
||||
readonly countries: readonly CountryInfo[];
|
||||
readonly subjects: readonly SubjectInfo[];
|
||||
readonly dayFrame: DayFrameInfo | null;
|
||||
readonly holidays: readonly HolidayInfo[];
|
||||
|
||||
@@ -54,11 +54,22 @@ function catalog(): CatalogResponse {
|
||||
],
|
||||
things: [],
|
||||
defaultMap,
|
||||
nameSets: [
|
||||
countries: [
|
||||
{
|
||||
defName: 'Russian',
|
||||
label: 'Russian',
|
||||
nativeLanguages: [{ defName: 'Russian', label: 'Russian' }],
|
||||
defName: 'Russia',
|
||||
label: 'Russia',
|
||||
nativeLanguages: [
|
||||
{ defName: 'RussianLanguage', label: 'Russian' },
|
||||
{ defName: 'BelarusianLanguage', label: 'Belarusian' },
|
||||
{ defName: 'UkrainianLanguage', label: 'Ukrainian' },
|
||||
],
|
||||
climatePresets: ['TemperateContinental'],
|
||||
},
|
||||
{
|
||||
defName: 'Nordic',
|
||||
label: 'Nordic',
|
||||
nativeLanguages: [{ defName: 'NorwegianLanguage', label: 'Norwegian' }],
|
||||
climatePresets: ['TemperateContinental'],
|
||||
},
|
||||
],
|
||||
subjects: [],
|
||||
@@ -162,6 +173,55 @@ describe('createSchoolDialog', () => {
|
||||
finish(school());
|
||||
await expect(opened).resolves.toMatchObject({ name: 'North' });
|
||||
});
|
||||
|
||||
it('shows countries and refreshes languages when the country changes', async () => {
|
||||
const opened = createSchoolDialog({
|
||||
defaultStartDate: new Date('2024-09-01T00:00:00.000Z'),
|
||||
suggestName: async () => 'North',
|
||||
create: async () => school(),
|
||||
});
|
||||
await vi.waitFor(() => expect(fetchCatalog).toHaveBeenCalled());
|
||||
const dialog = document.querySelector('dialog');
|
||||
if (dialog === null) {
|
||||
throw new Error('create dialog is missing');
|
||||
}
|
||||
|
||||
expect(byText(dialog, '.field__label', t('countryTitle'))).toBeTruthy();
|
||||
expect([...dialog.querySelectorAll('.field__label')].some((node) => node.textContent === 'Name set')).toBe(false);
|
||||
|
||||
const countrySelect = dialog.querySelector('select');
|
||||
if (!(countrySelect instanceof HTMLSelectElement)) {
|
||||
throw new Error('country select is missing');
|
||||
}
|
||||
|
||||
expect([...countrySelect.options].map((option) => option.value)).toEqual(['Russia', 'Nordic']);
|
||||
const nativeField = byText(dialog, '.field__label', t('nativeLanguageTitle')).closest('.field');
|
||||
if (!(nativeField instanceof HTMLElement)) {
|
||||
throw new Error('native language field is missing');
|
||||
}
|
||||
|
||||
expect(nativeField.hidden).toBe(false);
|
||||
|
||||
countrySelect.value = 'Nordic';
|
||||
countrySelect.dispatchEvent(new Event('change'));
|
||||
expect(nativeField.hidden).toBe(true);
|
||||
|
||||
countrySelect.value = 'Russia';
|
||||
countrySelect.dispatchEvent(new Event('change'));
|
||||
const nativeSelect = nativeField.querySelector('select');
|
||||
if (!(nativeSelect instanceof HTMLSelectElement)) {
|
||||
throw new Error('native language select is missing');
|
||||
}
|
||||
|
||||
expect([...nativeSelect.options].map((option) => option.value)).toEqual([
|
||||
'',
|
||||
'RussianLanguage',
|
||||
'BelarusianLanguage',
|
||||
'UkrainianLanguage',
|
||||
]);
|
||||
expect(nativeField.hidden).toBe(false);
|
||||
void opened;
|
||||
});
|
||||
});
|
||||
|
||||
describe('map reset from the create editor', () => {
|
||||
|
||||
@@ -32,7 +32,7 @@ export function createSchoolDialog(options: CreateSchoolOptions): Promise<School
|
||||
const extraModIds = new Set<string>();
|
||||
let catalog: CatalogResponse | null = null;
|
||||
let currentMap: MapLayout | null = null;
|
||||
let nameSetId: string | null = null;
|
||||
let countryId: string | null = null;
|
||||
let nativeLanguageId: string | null = null;
|
||||
|
||||
const modsField = el('div', { class: 'field' });
|
||||
@@ -40,11 +40,11 @@ export function createSchoolDialog(options: CreateSchoolOptions): Promise<School
|
||||
const modsList = el('div', { class: 'mod-list' });
|
||||
modsField.append(modsLabel, modsList);
|
||||
|
||||
const nameSetLabel = el('span', { class: 'field__label' });
|
||||
const nameSetSelect = el('select', { class: 'input' });
|
||||
const nameSetField = el('label', { class: 'field' }, nameSetLabel, nameSetSelect);
|
||||
nameSetSelect.addEventListener('change', () => {
|
||||
nameSetId = nameSetSelect.value === '' ? null : nameSetSelect.value;
|
||||
const countryLabel = el('span', { class: 'field__label' });
|
||||
const countrySelect = el('select', { class: 'input' });
|
||||
const countryField = el('label', { class: 'field' }, countryLabel, countrySelect);
|
||||
countrySelect.addEventListener('change', () => {
|
||||
countryId = countrySelect.value === '' ? null : countrySelect.value;
|
||||
nativeLanguageId = null;
|
||||
if (catalog !== null) {
|
||||
paintNativeLanguages(catalog);
|
||||
@@ -110,7 +110,7 @@ export function createSchoolDialog(options: CreateSchoolOptions): Promise<School
|
||||
el('label', { class: 'field' }, nameLabel, el('div', { class: 'field__row' }, nameInput, randomButton)),
|
||||
el('div', { class: 'field' }, startLabel, el('div', { class: 'field__row' }, dateInput, timeInput)),
|
||||
modsField,
|
||||
nameSetField,
|
||||
countryField,
|
||||
nativeField,
|
||||
mapField,
|
||||
seedField,
|
||||
@@ -125,7 +125,7 @@ export function createSchoolDialog(options: CreateSchoolOptions): Promise<School
|
||||
const waiting = value || catalog === null;
|
||||
submitButton.toggleAttribute('disabled', waiting);
|
||||
randomButton.toggleAttribute('disabled', value);
|
||||
nativeRandomButton.toggleAttribute('disabled', value || languagesOf(catalog, nameSetId).length <= 1);
|
||||
nativeRandomButton.toggleAttribute('disabled', value || languagesOf(catalog, countryId).length <= 1);
|
||||
editMapButton.toggleAttribute('disabled', waiting);
|
||||
seedInput.toggleAttribute('disabled', value);
|
||||
};
|
||||
@@ -149,31 +149,31 @@ export function createSchoolDialog(options: CreateSchoolOptions): Promise<School
|
||||
const applyCatalog = (next: CatalogResponse): void => {
|
||||
catalog = next;
|
||||
currentMap = structuredClone(next.defaultMap);
|
||||
const stillThere = next.nameSets.some((set) => set.defName === nameSetId);
|
||||
nameSetId = stillThere ? nameSetId : (next.nameSets[0]?.defName ?? null);
|
||||
if (!languagesOf(next, nameSetId).some((language) => language.defName === nativeLanguageId)) {
|
||||
const stillThere = next.countries.some((country) => country.defName === countryId);
|
||||
countryId = stillThere ? countryId : (next.countries[0]?.defName ?? null);
|
||||
if (!languagesOf(next, countryId).some((language) => language.defName === nativeLanguageId)) {
|
||||
nativeLanguageId = null;
|
||||
}
|
||||
paintNameSets(next);
|
||||
paintCountries(next);
|
||||
paintNativeLanguages(next);
|
||||
paintMapHint();
|
||||
setBusy(busy);
|
||||
};
|
||||
|
||||
const paintNameSets = (next: CatalogResponse): void => {
|
||||
nameSetSelect.replaceChildren();
|
||||
for (const set of next.nameSets) {
|
||||
const option = el('option', { text: set.label });
|
||||
option.value = set.defName;
|
||||
nameSetSelect.append(option);
|
||||
const paintCountries = (next: CatalogResponse): void => {
|
||||
countrySelect.replaceChildren();
|
||||
for (const country of next.countries) {
|
||||
const option = el('option', { text: country.label });
|
||||
option.value = country.defName;
|
||||
countrySelect.append(option);
|
||||
}
|
||||
|
||||
nameSetSelect.value = nameSetId ?? '';
|
||||
nameSetSelect.disabled = next.nameSets.length <= 1;
|
||||
countrySelect.value = countryId ?? '';
|
||||
countrySelect.disabled = next.countries.length <= 1;
|
||||
};
|
||||
|
||||
const paintNativeLanguages = (next: CatalogResponse): void => {
|
||||
const languages = languagesOf(next, nameSetId);
|
||||
const languages = languagesOf(next, countryId);
|
||||
nativeSelect.replaceChildren();
|
||||
const random = el('option', { text: t('nativeLanguageRandom') });
|
||||
random.value = '';
|
||||
@@ -187,7 +187,7 @@ export function createSchoolDialog(options: CreateSchoolOptions): Promise<School
|
||||
nativeSelect.value = nativeLanguageId ?? '';
|
||||
nativeSelect.disabled = languages.length <= 1;
|
||||
nativeRandomButton.toggleAttribute('disabled', busy || languages.length <= 1);
|
||||
nativeField.hidden = languages.length === 0;
|
||||
nativeField.hidden = languages.length <= 1;
|
||||
};
|
||||
|
||||
const reloadCatalog = async (): Promise<void> => {
|
||||
@@ -242,7 +242,7 @@ export function createSchoolDialog(options: CreateSchoolOptions): Promise<School
|
||||
const localize = (): void => {
|
||||
title.textContent = t('newSchool');
|
||||
modsLabel.textContent = t('modsTitle');
|
||||
nameSetLabel.textContent = t('nameSetTitle');
|
||||
countryLabel.textContent = t('countryTitle');
|
||||
nativeLabel.textContent = t('nativeLanguageTitle');
|
||||
nativeRandomButton.textContent = t('randomName');
|
||||
nativeRandomButton.title = t('randomNativeTitle');
|
||||
@@ -260,7 +260,7 @@ export function createSchoolDialog(options: CreateSchoolOptions): Promise<School
|
||||
cancelButton.textContent = t('cancel');
|
||||
paintMapHint();
|
||||
if (catalog !== null) {
|
||||
paintNameSets(catalog);
|
||||
paintCountries(catalog);
|
||||
paintNativeLanguages(catalog);
|
||||
}
|
||||
};
|
||||
@@ -284,7 +284,7 @@ export function createSchoolDialog(options: CreateSchoolOptions): Promise<School
|
||||
return;
|
||||
}
|
||||
|
||||
const languages = languagesOf(catalog, nameSetId);
|
||||
const languages = languagesOf(catalog, countryId);
|
||||
if (languages.length === 0) {
|
||||
return;
|
||||
}
|
||||
@@ -332,7 +332,7 @@ export function createSchoolDialog(options: CreateSchoolOptions): Promise<School
|
||||
.create(nameInput.value.trim(), startDate, {
|
||||
modIds: [...extraModIds],
|
||||
map: currentMap,
|
||||
nameSetId: nameSetId ?? undefined,
|
||||
countryId: countryId ?? undefined,
|
||||
nativeLanguage: nativeLanguageId ?? undefined,
|
||||
seed: seed ?? undefined,
|
||||
})
|
||||
@@ -349,12 +349,12 @@ export function createSchoolDialog(options: CreateSchoolOptions): Promise<School
|
||||
return modal.open(nameInput);
|
||||
}
|
||||
|
||||
function languagesOf(catalog: CatalogResponse | null, nameSetId: string | null): readonly { readonly defName: string; readonly label: string }[] {
|
||||
if (catalog === null || nameSetId === null) {
|
||||
function languagesOf(catalog: CatalogResponse | null, countryId: string | null): readonly { readonly defName: string; readonly label: string }[] {
|
||||
if (catalog === null || countryId === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return catalog.nameSets.find((set) => set.defName === nameSetId)?.nativeLanguages ?? [];
|
||||
return catalog.countries.find((country) => country.defName === countryId)?.nativeLanguages ?? [];
|
||||
}
|
||||
|
||||
function mapsEqual(left: MapLayout, right: MapLayout): boolean {
|
||||
@@ -401,8 +401,8 @@ function describe(reason: unknown): string {
|
||||
return t('errorModCycle');
|
||||
case 'invalid-catalog':
|
||||
return t('errorInvalidCatalog');
|
||||
case 'unknown-name-set':
|
||||
return t('errorUnknownNameSet');
|
||||
case 'unknown-country':
|
||||
return t('errorUnknownCountry');
|
||||
case 'unknown-native-language':
|
||||
return t('errorUnknownNativeLanguage');
|
||||
default:
|
||||
|
||||
Reference in New Issue
Block a user