diff --git a/packages/frontend/.storybook/fake-utils.ts b/packages/frontend/.storybook/fake-utils.ts index 93c8b2057a..c777cbbe72 100644 --- a/packages/frontend/.storybook/fake-utils.ts +++ b/packages/frontend/.storybook/fake-utils.ts @@ -3,6 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import seedrandom from 'seedrandom'; + /** * AIで生成した無作為なファーストネーム */ @@ -30,18 +32,21 @@ export const countryDict = [ 'Vietnam', 'Kenya', 'Saudi Arabia', 'Netherlands', 'Colombia', 'Poland', 'Chile', 'Malaysia', 'Ukraine', 'New Zealand', 'Peru', ] -export function text(length: number = 10): string { +export function text(length: number = 10, seed?: string): string { let result = ""; + // シード値を使う場合、同じ数値が羅列されるが、ランダム文字列という意味では満たせていると思うのでこのまま使っておく + const rand = seed ? seedrandom(seed)() : Math.random(); while (result.length < length) { - result += Math.random().toString(36).substring(2); + result += rand.toString(36).substring(2); } return result.substring(0, length); } -export function integer(min: number = 0, max: number = 9999): number { - return Math.floor(Math.random() * (max - min)) + min; +export function integer(min: number = 0, max: number = 9999, seed?: string): number { + const rand = seed ? seedrandom(seed)() : Math.random(); + return Math.floor(rand * (max - min)) + min; } export function date(params?: { @@ -59,10 +64,10 @@ export function date(params?: { secondMax?: number, millisecondMin?: number, millisecondMax?: number, -}) { - const year = integer(params?.yearMin ?? 1970, params?.yearMax ?? (new Date()).getFullYear()); - const month = integer(params?.monthMin ?? 1, params?.monthMax ?? 12); - let day = integer(params?.dayMin ?? 1, params?.dayMax ?? 31); +}, seed?: string): Date { + const year = integer(params?.yearMin ?? 1970, params?.yearMax ?? (new Date()).getFullYear(), seed); + const month = integer(params?.monthMin ?? 1, params?.monthMax ?? 12, seed); + let day = integer(params?.dayMin ?? 1, params?.dayMax ?? 31, seed); if (month === 2) { day = Math.min(day, 28); } else if ([4, 6, 9, 11].includes(month)) { @@ -71,43 +76,45 @@ export function date(params?: { day = Math.min(day, 31); } - const hour = integer(params?.hourMin ?? 0, params?.hourMax ?? 23); - const minute = integer(params?.minuteMin ?? 0, params?.minuteMax ?? 59); - const second = integer(params?.secondMin ?? 0, params?.secondMax ?? 59); - const millisecond = integer(params?.millisecondMin ?? 0, params?.millisecondMax ?? 999); + const hour = integer(params?.hourMin ?? 0, params?.hourMax ?? 23, seed); + const minute = integer(params?.minuteMin ?? 0, params?.minuteMax ?? 59, seed); + const second = integer(params?.secondMin ?? 0, params?.secondMax ?? 59, seed); + const millisecond = integer(params?.millisecondMin ?? 0, params?.millisecondMax ?? 999, seed); return new Date(year, month - 1, day, hour, minute, second, millisecond); } -export function boolean(): boolean { - return Math.random() < 0.5; +export function boolean(seed?: string): boolean { + const rand = seed ? seedrandom(seed)() : Math.random(); + return rand < 0.5; } -export function choose(array: T[]): T { - return array[Math.floor(Math.random() * array.length)]; +export function choose(array: T[], seed?: string): T { + const rand = seed ? seedrandom(seed)() : Math.random(); + return array[Math.floor(rand * array.length)]; } -export function firstName(): string { - return choose(firstNameDict); +export function firstName(seed?: string): string { + return choose(firstNameDict, seed); } -export function lastName(): string { - return choose(lastNameDict); +export function lastName(seed?: string): string { + return choose(lastNameDict, seed); } -export function country(): string { - return choose(countryDict); +export function country(seed?: string): string { + return choose(countryDict, seed); } const TIME2000 = 946684800000; -export function fakeId(): string { +export function fakeId(seed?: string): string { let time = new Date().getTime(); time = time - TIME2000; if (time < 0) time = 0; const timeStr = time.toString(36).padStart(8, '0'); - const noiseStr = text(2); + const noiseStr = text(2, seed); return timeStr + noiseStr; } @@ -123,7 +130,7 @@ export function imageDataUrl(options?: { blue?: number, alpha?: number, } -}): string { +}, seed?: string): string { const canvas = document.createElement('canvas'); canvas.width = options?.size?.width ?? 100; canvas.height = options?.size?.height ?? 100; @@ -135,9 +142,9 @@ export function imageDataUrl(options?: { ctx.beginPath() - const red = options?.color?.red ?? integer(0, 255); - const green = options?.color?.green ?? integer(0, 255); - const blue = options?.color?.blue ?? integer(0, 255); + const red = options?.color?.red ?? integer(0, 255, seed); + const green = options?.color?.green ?? integer(0, 255, seed); + const blue = options?.color?.blue ?? integer(0, 255, seed); const alpha = options?.color?.alpha ?? 1; ctx.arc(canvas.width / 2, canvas.height / 2, canvas.width / 2, 0, Math.PI * 2, true); ctx.fillStyle = `rgba(${red}, ${green}, ${blue}, ${alpha})`; diff --git a/packages/frontend/.storybook/fakes.ts b/packages/frontend/.storybook/fakes.ts index 15d410f90b..c900c77d54 100644 --- a/packages/frontend/.storybook/fakes.ts +++ b/packages/frontend/.storybook/fakes.ts @@ -4,7 +4,7 @@ */ import type { entities } from 'misskey-js' -import { imageDataUrl, text } from "./fake-utils.js"; +import { date, imageDataUrl, text } from "./fake-utils.js"; export function abuseUserReport() { return { @@ -232,9 +232,11 @@ export function role(params: { asBadge?: boolean, canEditMembersByModerator?: boolean, usersCount?: number, -}): entities.Role { +}, seed?: string): entities.Role { const prefix = params.displayOrder ? params.displayOrder.toString().padStart(3, '0') + '-' : ''; - const genId = Math.random().toString(36).substring(10); + const genId = text(36, seed); + const createdAt = params.createdAt ?? date({}, seed).toISOString(); + const updatedAt = params.updatedAt ?? date({}, seed).toISOString(); return { id: params.id ?? genId, @@ -245,8 +247,8 @@ export function role(params: { isModerator: params.isModerator ?? false, isAdministrator: params.isAdministrator ?? false, displayOrder: params.displayOrder ?? 0, - createdAt: params.createdAt ?? new Date().toISOString(), - updatedAt: params.updatedAt ?? new Date().toISOString(), + createdAt: createdAt, + updatedAt: updatedAt, target: params.target ?? 'manual', isPublic: params.isPublic ?? true, isExplorable: params.isExplorable ?? true, @@ -277,11 +279,13 @@ export function emoji(params?: { localOnly?: boolean, roleIdsThatCanBeUsedThisEmojiAsReaction?: {id:string, name:string}[], updatedAt?: string, -}): entities.EmojiDetailedAdmin { - const id = params?.id ?? new Date().getTime().toString() + text(5); - const name = params?.name ?? text(8); +}, seed?: string): entities.EmojiDetailedAdmin { + const _seed = seed ?? (params?.id ?? "DEFAULT_SEED"); + const id = params?.id ?? text(32, _seed); + const name = params?.name ?? text(8, _seed); + const updatedAt = params?.updatedAt ?? date({}, _seed).toISOString(); - const image = imageDataUrl() + const image = imageDataUrl({}, _seed) return { id: id, @@ -297,6 +301,6 @@ export function emoji(params?: { isSensitive: params?.isSensitive ?? false, localOnly: params?.localOnly ?? false, roleIdsThatCanBeUsedThisEmojiAsReaction: params?.roleIdsThatCanBeUsedThisEmojiAsReaction ?? [], - updatedAt: params?.updatedAt ?? new Date().toISOString(), + updatedAt: updatedAt, } } diff --git a/packages/frontend/src/components/MkRoleSelectDialog.stories.impl.ts b/packages/frontend/src/components/MkRoleSelectDialog.stories.impl.ts index f193233285..411d62edf9 100644 --- a/packages/frontend/src/components/MkRoleSelectDialog.stories.impl.ts +++ b/packages/frontend/src/components/MkRoleSelectDialog.stories.impl.ts @@ -11,10 +11,10 @@ import { commonHandlers } from '../../.storybook/mocks.js'; import MkRoleSelectDialog from '@/components/MkRoleSelectDialog.vue'; const roles = [ - role({ displayOrder: 1 }), role({ displayOrder: 1 }), role({ displayOrder: 1 }), role({ displayOrder: 1 }), - role({ displayOrder: 2 }), role({ displayOrder: 2 }), role({ displayOrder: 3 }), role({ displayOrder: 3 }), - role({ displayOrder: 4 }), role({ displayOrder: 5 }), role({ displayOrder: 6 }), role({ displayOrder: 7 }), - role({ displayOrder: 999, name: 'privateRole', isPublic: false }), + role({ displayOrder: 1 }, '1'), role({ displayOrder: 1 }, '1'), role({ displayOrder: 1 }, '1'), role({ displayOrder: 1 }, '1'), + role({ displayOrder: 2 }, '2'), role({ displayOrder: 2 }, '2'), role({ displayOrder: 3 }, '3'), role({ displayOrder: 3 }, '3'), + role({ displayOrder: 4 }, '4'), role({ displayOrder: 5 }, '5'), role({ displayOrder: 6 }, '6'), role({ displayOrder: 7 }, '7'), + role({ displayOrder: 999, name: 'privateRole', isPublic: false }, '999'), ]; export const Default = { diff --git a/packages/frontend/src/components/grid/MkGrid.stories.impl.ts b/packages/frontend/src/components/grid/MkGrid.stories.impl.ts index 7ad8117722..5801012f15 100644 --- a/packages/frontend/src/components/grid/MkGrid.stories.impl.ts +++ b/packages/frontend/src/components/grid/MkGrid.stories.impl.ts @@ -12,6 +12,7 @@ import { boolean, choose, country, date, firstName, integer, lastName, text } fr import MkGrid from './MkGrid.vue'; import { GridContext, GridEvent } from '@/components/grid/grid-event.js'; import { DataSource, GridSetting } from '@/components/grid/grid.js'; +import { GridColumnSetting } from '@/components/grid/column.js'; function d(p: { check?: boolean, @@ -23,23 +24,23 @@ function d(p: { country?: string, reportCount?: number, createdAt?: string, -}) { - const prefix = text(10); +}, seed: string) { + const prefix = text(10, seed); return { - check: p.check ?? boolean(), - name: p.name ?? `${firstName()} ${lastName()}`, + check: p.check ?? boolean(seed), + name: p.name ?? `${firstName(seed)} ${lastName(seed)}`, email: p.email ?? `${prefix}@example.com`, - age: p.age ?? integer(20, 80), - birthday: date().toISOString(), - gender: p.gender ?? choose(['male', 'female', 'other', 'unknown']), - country: p.country ?? country(), - reportCount: p.reportCount ?? integer(), - createdAt: p.createdAt ?? date().toISOString(), + age: p.age ?? integer(20, 80, seed), + birthday: date({}, seed).toISOString(), + gender: p.gender ?? choose(['male', 'female', 'other', 'unknown'], seed), + country: p.country ?? country(seed), + reportCount: p.reportCount ?? integer(0, 9999, seed), + createdAt: p.createdAt ?? date({}, seed).toISOString(), }; } -const defaultCols = [ +const defaultCols: GridColumnSetting[] = [ { bindTo: 'check', icon: 'ti-check', type: 'boolean', width: 50 }, { bindTo: 'name', title: 'Name', type: 'text', width: 'auto' }, { bindTo: 'email', title: 'Email', type: 'text', width: 'auto' }, @@ -52,13 +53,10 @@ const defaultCols = [ ]; function createArgs(overrides?: { settings?: Partial, data?: DataSource[] }) { - const refData = ref([ - d({}), d({}), d({}), d({}), d({}), d({}), d({}), d({}), d({}), d({}), d({}), - d({}), d({}), d({}), d({}), d({}), d({}), d({}), d({}), d({}), d({}), d({}), - d({}), d({}), d({}), d({}), d({}), d({}), d({}), d({}), d({}), d({}), d({}), - d({}), d({}), d({}), d({}), d({}), d({}), d({}), d({}), d({}), d({}), d({}), - d({}), d({}), d({}), d({}), d({}), d({}), d({}), d({}), d({}), d({}), d({}), - ]); + const refData = ref[]>([]); + for (let i = 0; i < 100; i++) { + refData.value.push(d({}, i.toString())); + } return { settings: { @@ -73,7 +71,7 @@ function createArgs(overrides?: { settings?: Partial, data?: DataSo }; } -function createRender(params: { settings: Partial, data: DataSource[] }) { +function createRender(params: { settings: GridSetting, data: DataSource[] }) { return { render(args) { return { @@ -155,7 +153,7 @@ export const AdditionalRowStyle = createRender(createArgs({ row: { styleRules: [ { - condition: ({ row }) => AdditionalRowStyle.args.data[row.index].check, + condition: ({ row }) => AdditionalRowStyle.args.data[row.index].check as boolean, applyStyle: { style: { backgroundColor: 'lightgray', diff --git a/packages/frontend/src/pages/admin/custom-emojis-manager2.stories.impl.ts b/packages/frontend/src/pages/admin/custom-emojis-manager2.stories.impl.ts index 373f1d3211..7b0d66ee3a 100644 --- a/packages/frontend/src/pages/admin/custom-emojis-manager2.stories.impl.ts +++ b/packages/frontend/src/pages/admin/custom-emojis-manager2.stories.impl.ts @@ -89,7 +89,7 @@ function createRender(params: { }); const driveFile: entities.DriveFile = { - id: fakeId(), + id: fakeId(file.name), createdAt: new Date().toISOString(), name: file.name, type: file.type, @@ -122,7 +122,7 @@ function createRender(params: { const file = storedDriveFiles.find(f => f.id === fileId)!; const em = emoji({ - id: fakeId(), + id: fakeId(file.name), name: body.name, publicUrl: file.url, originalUrl: file.url, @@ -148,13 +148,13 @@ export const Default = createRender({ }); export const List10 = createRender({ - emojis: Array.from({ length: 10 }, (_, i) => emoji({ name: `emoji_${i}` })), + emojis: Array.from({ length: 10 }, (_, i) => emoji({ name: `emoji_${i}` }, i.toString())), }); export const List100 = createRender({ - emojis: Array.from({ length: 100 }, (_, i) => emoji({ name: `emoji_${i}` })), + emojis: Array.from({ length: 100 }, (_, i) => emoji({ name: `emoji_${i}` }, i.toString())), }); export const List1000 = createRender({ - emojis: Array.from({ length: 1000 }, (_, i) => emoji({ name: `emoji_${i}` })), + emojis: Array.from({ length: 1000 }, (_, i) => emoji({ name: `emoji_${i}` }, i.toString())), });