From 2009535f1a67e4eb77ab45bef41bf7ebdf004f5e Mon Sep 17 00:00:00 2001 From: "gorbenko.pavel" Date: Fri, 2 Oct 2026 13:50:57 +0500 Subject: [PATCH] =?UTF-8?q?fix:=20=D0=BF=D0=BE=D0=B8=D1=81=D0=BA=20=D1=81?= =?UTF-8?q?=D0=BE=D1=82=D1=80=D1=83=D0=B4=D0=BD=D0=B8=D0=BA=D0=B0=20=D0=BD?= =?UTF-8?q?=D0=B5=20=D0=BD=D0=B0=D1=85=D0=BE=D0=B4=D0=B8=D0=BB,=20=D0=B5?= =?UTF-8?q?=D1=81=D0=BB=D0=B8=20=D0=BF=D0=BE=D1=81=D0=BB=D0=B5=20=D0=B8?= =?UTF-8?q?=D0=BC=D0=B5=D0=BD=D0=B8=20=D0=B8=D0=BB=D0=B8=20=D1=84=D0=B0?= =?UTF-8?q?=D0=BC=D0=B8=D0=BB=D0=B8=D0=B8=20=D1=81=D1=82=D0=BE=D0=B8=D1=82?= =?UTF-8?q?=20=D0=BF=D1=80=D0=BE=D0=B1=D0=B5=D0=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PeoplePicker шлёт введённый текст в SharePoint как есть. Теперь в запросе поиска пробелы по краям обрезаются, а двойные внутри схлопываются. Co-Authored-By: Claude Sonnet 5.5 --- .../ui/PeopleField/pickerContext.test.ts | 27 +++++++++++++ .../shared/ui/PeopleField/pickerContext.ts | 39 ++++++++++++++++++- 2 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 src/webparts/requestPortal/shared/ui/PeopleField/pickerContext.test.ts diff --git a/src/webparts/requestPortal/shared/ui/PeopleField/pickerContext.test.ts b/src/webparts/requestPortal/shared/ui/PeopleField/pickerContext.test.ts new file mode 100644 index 0000000..9d7be02 --- /dev/null +++ b/src/webparts/requestPortal/shared/ui/PeopleField/pickerContext.test.ts @@ -0,0 +1,27 @@ +import { trimPickerQuery } from './pickerContext'; + +const body = (q: unknown): string => JSON.stringify({ queryParams: { QueryString: q, PrincipalType: 1 } }); +const query = (b: unknown): unknown => (JSON.parse(b as string) as { queryParams: { QueryString: unknown } }).queryParams.QueryString; + +describe('trimPickerQuery', () => { + it('убирает пробел в конце и в начале', () => { + expect(query(trimPickerQuery(body('Иван ')))).toBe('Иван'); + expect(query(trimPickerQuery(body(' Иван Петров ')))).toBe('Иван Петров'); + }); + + it('схлопывает двойные пробелы внутри', () => { + expect(query(trimPickerQuery(body('Иван Петров')))).toBe('Иван Петров'); + }); + + it('не трогает остальные поля запроса', () => { + const parsed = JSON.parse(trimPickerQuery(body('a ')) as string) as { queryParams: { PrincipalType: number } }; + expect(parsed.queryParams.PrincipalType).toBe(1); + }); + + it('чужие тела возвращает как есть', () => { + expect(trimPickerQuery('не json QueryString')).toBe('не json QueryString'); + expect(trimPickerQuery(undefined)).toBeUndefined(); + const other = JSON.stringify({ a: 1 }); + expect(trimPickerQuery(other)).toBe(other); + }); +}); diff --git a/src/webparts/requestPortal/shared/ui/PeopleField/pickerContext.ts b/src/webparts/requestPortal/shared/ui/PeopleField/pickerContext.ts index 021a13c..31aceed 100644 --- a/src/webparts/requestPortal/shared/ui/PeopleField/pickerContext.ts +++ b/src/webparts/requestPortal/shared/ui/PeopleField/pickerContext.ts @@ -18,6 +18,43 @@ export function pickedLogin(user: IPickedUser): string { return user.secondaryText || user.loginName || user.id || ''; } +/** + * Убирает пробелы по краям (и схлопывает внутренние) у QueryString в теле запроса + * поиска сотрудника. PeoplePicker из @pnp/spfx-controls-react шлёт введённый + * текст как есть, а SharePoint с «Иван » (пробел в конце, обычное дело при вводе + * «имя фамилия») ничего не находит. Тело не JSON или без QueryString — возвращается как было. + */ +export function trimPickerQuery(body: unknown): unknown { + if (typeof body !== 'string' || body.indexOf('QueryString') < 0) return body; + try { + const parsed = JSON.parse(body) as { queryParams?: { QueryString?: unknown } }; + const query = parsed.queryParams?.QueryString; + if (typeof query !== 'string') return body; + parsed.queryParams!.QueryString = query.trim().replace(/\s+/g, ' '); + return JSON.stringify(parsed); + } catch { + return body; + } +} + +/** + * spHttpClient с подчисткой запроса поиска (см. trimPickerQuery): прокси, чтобы + * остальные методы клиента работали как прежде, подменяется только post. + */ +function withTrimmedSearch(client: WebPartContext['spHttpClient']): WebPartContext['spHttpClient'] { + return new Proxy(client, { + get(target, prop, receiver) { + if (prop !== 'post') return Reflect.get(target, prop, receiver); + return (url: string, config: unknown, options?: { body?: unknown }) => + (target.post as (u: string, c: unknown, o?: unknown) => unknown)( + url, + config, + options ? { ...options, body: trimPickerQuery(options.body) } : options + ); + } + }); +} + /** * Контекст для PeoplePicker из WebPartContext. * as unknown: @pnp/spfx-controls-react тянет свою копию sp-http-msgraph, @@ -27,6 +64,6 @@ export function buildPickerContext(context: WebPartContext): IPeoplePickerContex return { absoluteUrl: context.pageContext.web.absoluteUrl, msGraphClientFactory: context.msGraphClientFactory, - spHttpClient: context.spHttpClient + spHttpClient: withTrimmedSearch(context.spHttpClient) } as unknown as IPeoplePickerContext; }