Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
2190694
[UIK-4606][notice-global] deprecate component (#2630)
slizhevskyv-semrush Dec 16, 2025
8fea293
[UIK-4623][data-table] fixed virtual scroll for tables with unknown r…
ilyabrower Dec 16, 2025
da43009
[carousel] fixed indicator style (#2626)
sheila-semrush Dec 17, 2025
829f8ba
UIK-4610/allure (#2639)
Valeria-Zimnitskaya Dec 17, 2025
a2ee282
[drag-and-drop] skip unstable test steps for firefox
Valeria-Zimnitskaya Dec 17, 2025
df8bbce
[drag-and-drop] fix unstable test
Valeria-Zimnitskaya Dec 17, 2025
9d403cd
[UIK-4619][chore] added translations (#2640)
ilyabrower Dec 17, 2025
37f1e68
[chore] fixed deps generator in ui package
ilyabrower Dec 17, 2025
1357916
[chore] Merge branch 'release/v16' of github.com:semrush/intergalacti…
ilyabrower Dec 17, 2025
a15eb8c
[chore] moved closeTasks and sendReleaseNotes to separate tasks
ilyabrower Dec 17, 2025
efc2f96
[chore] moved publish release notes to separate tasks
ilyabrower Dec 17, 2025
f0715f3
[UIK-3464][time-picker] rewrote component to ts/refactoring time pick…
slizhevskyv-semrush Dec 17, 2025
647997e
[UIK-4606][notice-global] deprecate component (#2630)
slizhevskyv-semrush Dec 16, 2025
21779f9
[UIK-4623][data-table] fixed virtual scroll for tables with unknown r…
ilyabrower Dec 16, 2025
6c80b3f
[carousel] fixed indicator style (#2626)
sheila-semrush Dec 17, 2025
d4babde
UIK-4610/allure (#2639)
Valeria-Zimnitskaya Dec 17, 2025
c3af396
[drag-and-drop] skip unstable test steps for firefox
Valeria-Zimnitskaya Dec 17, 2025
24d9d07
[chore] fixed deps generator in ui package
ilyabrower Dec 17, 2025
822c5fe
[drag-and-drop] fix unstable test
Valeria-Zimnitskaya Dec 17, 2025
e80a828
[UIK-4619][chore] added translations (#2640)
ilyabrower Dec 17, 2025
2fc4135
[chore] moved closeTasks and sendReleaseNotes to separate tasks
ilyabrower Dec 17, 2025
11d7394
[chore] moved publish release notes to separate tasks
ilyabrower Dec 17, 2025
199b49f
[data-table] skip test for webkit
Valeria-Zimnitskaya Dec 17, 2025
3e555a6
Merge remote-tracking branch 'origin/release/v16' into UIK-3464/time-…
slizhevskyv-semrush Dec 17, 2025
6630411
[UIK-3464][time-picker] added reactive/changeOnProps decorators and u…
slizhevskyv-semrush Dec 23, 2025
3dc1d2d
Merge remote-tracking branch 'origin/release/v16' into UIK-3464/time-…
slizhevskyv-semrush Dec 23, 2025
18b5dd4
[UIK-3464][core] uncommented test for @callOnPropsChange
slizhevskyv-semrush Dec 23, 2025
dd31537
[UIK-3464][time-picker] updated tests/small pr fixes
slizhevskyv-semrush Dec 29, 2025
633823c
[UIK-3464][time-picker] new trackPropsChanges decorator for class/uni…
slizhevskyv-semrush Dec 29, 2025
1b976c4
[UIK-3464][time-picker] updated import to @semcore/core
slizhevskyv-semrush Dec 29, 2025
63df0d2
[UIK-3464][time-picker] removed watchProps references
slizhevskyv-semrush Dec 29, 2025
ad658b0
[UIK-3464][time-picker] removed watchProps references
slizhevskyv-semrush Dec 29, 2025
442740d
[UIK-3464][time-picker] updated unit tests
slizhevskyv-semrush Jan 7, 2026
9889e54
[UIK-3464][time-picker] updated unit tests
slizhevskyv-semrush Jan 9, 2026
ba7b78f
[UIK-3464][time-picker] updated unit tests/reactive decorator logic (…
slizhevskyv-semrush Jan 9, 2026
3fb65a6
Update semcore/time-picker/src/entity/TimePickerEntity.ts
slizhevskyv-semrush Jan 12, 2026
52d9a82
[UIK-3464][time-picker] PR updates
slizhevskyv-semrush Jan 12, 2026
ceebe7d
[UIK-3464][time-picker] removed tests for getters/setters for TimePic…
slizhevskyv-semrush Jan 12, 2026
6166d2e
[UIK-3464][time-picker] PR updates
slizhevskyv-semrush Jan 12, 2026
e19f026
Merge remote-tracking branch 'origin/release/v17' into UIK-3464/time-…
slizhevskyv-semrush Jan 13, 2026
dd888b9
[UIK-3464][time-picker] type updates
slizhevskyv-semrush Jan 13, 2026
ec1a4dc
[UIK-3464][time-picker] added default value for TimePickerEntity.is12…
slizhevskyv-semrush Jan 13, 2026
379bdc7
[UIK-3464][time-picker] updated snapshots
slizhevskyv-semrush Jan 14, 2026
5e09c3d
[UIK-3464][time-picker] updated propsObserver decorator to handle pro…
slizhevskyv-semrush Jan 14, 2026
796f0ac
Merge branch 'release/v17' into UIK-3464/time-picker-on-change-api
Valeria-Zimnitskaya Jan 19, 2026
bb840ae
[time-picker] test and test story
Valeria-Zimnitskaya Jan 22, 2026
68e557f
Merge remote-tracking branch 'origin/release/v17' into UIK-3464/time-…
slizhevskyv-semrush Jan 23, 2026
4e8fb23
[UIK-3464][time-picker] fix format input/props propagation issues
slizhevskyv-semrush Jan 23, 2026
f493284
[UIK-3464][chore] update time picker story due to a11y error
slizhevskyv-semrush Jan 23, 2026
b1bba2d
[UIK-3464][time-picker] a11y update
slizhevskyv-semrush Jan 23, 2026
e021aea
[UIK-3464][time-picker] a11y update
slizhevskyv-semrush Jan 23, 2026
088384b
Merge branch 'release/v17' into UIK-3464/time-picker-on-change-api
Valeria-Zimnitskaya Jan 23, 2026
f64527c
[widget-empty] update snapshots
Valeria-Zimnitskaya Jan 23, 2026
ac769c4
[time-picker] update tests
Valeria-Zimnitskaya Jan 23, 2026
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
35 changes: 18 additions & 17 deletions semcore/core/__tests__/decorators.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { describe, it, expect, vi, Test } from '@semcore/testing-utils/vitest';

import propsObserver from '../src/decorators/propsObserver';
import reactive from '../src/decorators/reactive';
import trackPropsChanges from '../src/decorators/trackPropsChanges';

interface TestProps {
name: string;
Expand Down Expand Up @@ -31,7 +31,7 @@ describe('@reactive', () => {
class TestClass {
name = 'TestInstance';

@reactive(function (this: TestClass) {
@reactive(function () {
capturedThis = this;
})
value = 10;
Expand Down Expand Up @@ -78,6 +78,7 @@ describe('@reactive', () => {
expect(callback).toHaveBeenCalledWith('name', 'Jane');
expect(callback).toHaveBeenCalledTimes(1);
});

it('should NOT call callback when non-watched field changes', () => {
const callback = vi.fn();

Expand Down Expand Up @@ -118,7 +119,7 @@ describe('@reactive', () => {
class TestClass {
id = 'test-123';

@reactive(['value'], function (this: TestClass) {
@reactive(['value'], function () {
capturedThis = this;
})
readonly data = { value: 0 };
Expand Down Expand Up @@ -193,16 +194,17 @@ describe('@reactive', () => {

class TestClass {
@reactive([], callback)
readonly data = { value: 0 };
readonly data = { value: 0, name: 'test' };
}

const instance = new TestClass();
instance.data.value = 42;
instance.data.name = 'updated name';

expect(callback).toHaveBeenCalledOnce();
expect(callback).toHaveBeenCalledTimes(2);
});

it('should work when setting same value multiple times', () => {
it('should call only once for the same value', () => {
const callback = vi.fn();

class TestClass {
Expand All @@ -220,12 +222,12 @@ describe('@reactive', () => {
});
});

describe('@trackPropsChanges', () => {
describe('@propsObserver', () => {
describe('Basic functionality', () => {
it('should track specified props changes', () => {
const onPropsChangeSpy = vi.fn();

@trackPropsChanges(['name', 'age'])
@propsObserver(['name', 'age'])
class TestComponent {
props: TestProps;

Expand Down Expand Up @@ -257,7 +259,7 @@ describe('@trackPropsChanges', () => {
it('should detect multiple props changes', () => {
const onPropsChangeSpy = vi.fn();

@trackPropsChanges(['name', 'age'])
@propsObserver(['name', 'age'])
class TestComponent {
props: TestProps;

Expand Down Expand Up @@ -286,7 +288,7 @@ describe('@trackPropsChanges', () => {
it('should not call onPropsChange when no tracked props changed', () => {
const onPropsChangeSpy = vi.fn();

@trackPropsChanges(['name'])
@propsObserver(['name'])
class TestComponent {
props: TestProps;

Expand Down Expand Up @@ -321,7 +323,7 @@ describe('@trackPropsChanges', () => {
user: { name: string };
}

@trackPropsChanges(['user'])
@propsObserver(['user'])
class TestComponent {
props: ObjectProps;

Expand Down Expand Up @@ -358,7 +360,7 @@ describe('@trackPropsChanges', () => {
items: string[];
}

@trackPropsChanges(['items'])
@propsObserver(['items'])
class TestComponent {
props: ArrayProps;

Expand Down Expand Up @@ -390,7 +392,7 @@ describe('@trackPropsChanges', () => {
it('should handle empty propsToWatch array', () => {
const onPropsChangeSpy = vi.fn();

@trackPropsChanges([])
@propsObserver([])
class TestComponent {
props: TestProps;

Expand All @@ -408,19 +410,18 @@ describe('@trackPropsChanges', () => {
}

const component = new TestComponent({ name: 'John', age: 30 });

component.render();

component.props = { name: 'Jane', age: 25 };
component.render();

expect(onPropsChangeSpy).not.toHaveBeenCalled();
expect(onPropsChangeSpy).toHaveBeenCalledTimes(1);
});

it('should work with multiple renders without prop changes', () => {
const onPropsChangeSpy = vi.fn();

@trackPropsChanges(['name'])
@propsObserver(['name'])
class TestComponent {
props: TestProps;

Expand Down Expand Up @@ -451,7 +452,7 @@ describe('@trackPropsChanges', () => {
it('should call onPropsChange before parent render', () => {
const callOrder: string[] = [];

@trackPropsChanges(['name'])
@propsObserver(['name'])
class TestComponent {
props: TestProps;

Expand Down
10 changes: 10 additions & 0 deletions semcore/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,16 @@
"require": "./lib/utils/use/useScrollBarWidth.js",
"import": "./lib/utils/use/useScrollBarWidth.mjs",
"types": "./lib/utils/use/useScrollBarWidth.d.ts"
},
"./lib/decorators/reactive": {
"require": "./lib/decorators/reactive.js",
"import": "./lib/decorators/reactive.mjs",
"types": "./lib/decorators/reactive.d.ts"
},
"./lib/decorators/propsObserver": {
"require": "./lib/decorators/propsObserver.js",
"import": "./lib/decorators/propsObserver.mjs",
"types": "./lib/decorators/propsObserver.d.ts"
}
},
"dependencies": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
type WatchedProps<Props> = { [key in keyof Props]?: any };
type WatchedProps<Props> = { [key in keyof Props]?: unknown };

type Constructor<Props> = new (...args: any) => {
type Constructor<Props> = new (...args: any[]) => {
props: Props;
onPropsChange(changedProps: WatchedProps<Props>): void;
render(): any;
render(): React.ReactNode;
};

function trackPropsChanges<
function propsObserver<
P,
C extends Constructor<P> = Constructor<P>,
>(propsToWatch: Array<keyof P>) {
Expand All @@ -17,18 +17,20 @@ function trackPropsChanges<
constructor(...args: any[]) {
super(...args);

propsToWatch.reduce((acc, prop) => {
acc[prop] = this.props?.[prop];
if (!this.props) return;

return acc;
}, watchedProps);
const propKeys = propsToWatch.length === 0 ? Object.keys(this.props) as Array<keyof P> : [...propsToWatch];

propKeys.forEach((prop) => {
watchedProps[prop] = this.props[prop];
});
}

onPropsChange(_?: WatchedProps<P>) {
let shouldCallFunc = false;
const changedProps: WatchedProps<P> = {};

propsToWatch.forEach((prop) => {
(Object.keys(watchedProps) as Array<keyof P>).forEach((prop) => {
const isPropValueEqual = Object.is(watchedProps[prop], this.props[prop]);

if (!isPropValueEqual) {
Expand All @@ -53,4 +55,4 @@ function trackPropsChanges<
};
}

export default trackPropsChanges;
export default propsObserver;
4 changes: 2 additions & 2 deletions semcore/core/src/decorators/reactive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ type IsReadonly<This, Property extends keyof This> =
(<F>() => F extends { -readonly [P in Property]: This[Property] } ? 1 : 2)
? false
: true;
type Callback<This> = (this: This, field: string | symbol, newValue: any) => void;
type Callback<This> = (this: This, field: string | symbol, newValue: unknown) => void;
type ReturnType<
This,
Property extends keyof This = keyof This,
Expand All @@ -14,7 +14,7 @@ type ReturnType<
? (_: undefined, ctx: ClassFieldDecoratorContext<This, This[Property]>) => void
: never;

const isPrimitiveValue = (value: any) => value !== Object(value);
const isPrimitiveValue = (value: unknown) => value !== Object(value);

function reactive<This>(cb: Callback<This>): ReturnType<This>;
function reactive<
Expand Down
58 changes: 4 additions & 54 deletions semcore/time-picker/__tests__/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,6 @@ describe('TimePickerEntity', () => {

expect(entity.hours).toBe('02');
});

it('should format 23:59 as 11:59', () => {
const entity = new TimePickerEntity('23:59', true);

expect(entity.hours).toBe('11');
expect(entity.minutes).toBe('59');
});
});

describe('24-hour format', () => {
Expand All @@ -86,74 +79,32 @@ describe('TimePickerEntity', () => {

it('should convert 12 AM to 00:00 in 24-hour format', () => {
const entity = new TimePickerEntity('12:00', false);
entity.meridiem = 'AM';

expect(entity.hours).toBe('00');
});

it('should convert 12 PM to 12:00 in 24-hour format', () => {
const entity = new TimePickerEntity('12:00', false);
entity.meridiem = 'PM';
entity.toggleMeridiem();

expect(entity.hours).toBe('12');
});

it('should convert PM hours correctly', () => {
const entity = new TimePickerEntity('3:00', false);
entity.meridiem = 'PM';
entity.toggleMeridiem();

expect(entity.hours).toBe('15');
});

it('should keep AM hours unchanged (except 12)', () => {
const entity = new TimePickerEntity('9:00', false);
entity.meridiem = 'AM';

expect(entity.hours).toBe('09');
});
});

describe('getters and setters', () => {
it('should update hours via setter', () => {
const entity = new TimePickerEntity('10:00', false);
entity.hours = '15';

expect(entity.hours).toBe('15');
});

it('should update minutes via setter', () => {
const entity = new TimePickerEntity('10:00', false);
entity.minutes = '45';

expect(entity.minutes).toBe('45');
});

it('should update meridiem via setter', () => {
const entity = new TimePickerEntity('10:00', true);
entity.meridiem = 'PM';

expect(entity.meridiem).toBe('PM');
});
});

describe('toggleMeridiem', () => {
it('should toggle from AM to PM', () => {
const entity = new TimePickerEntity('10:00', true);

entity.toggleMeridiem();

expect(entity.meridiem).toBe('PM');
});

it('should toggle from PM to AM', () => {
const entity = new TimePickerEntity('10:00', true);
entity.meridiem = 'PM';

entity.toggleMeridiem();

expect(entity.meridiem).toBe('AM');
});

it('should toggle multiple times correctly', () => {
const entity = new TimePickerEntity('10:00', true);

Expand All @@ -177,21 +128,20 @@ describe('TimePickerEntity', () => {

it('should convert to 24-hour format string when is12Hour is true', () => {
const entity = new TimePickerEntity('2:30', true);
entity.meridiem = 'PM';
entity.toggleMeridiem();

expect(entity.toString()).toBe('14:30');
});

it('should handle midnight (12 AM) conversion', () => {
const entity = new TimePickerEntity('12:00', true);
entity.meridiem = 'AM';

expect(entity.toString()).toBe('00:00');
});

it('should handle noon (12 PM) conversion', () => {
const entity = new TimePickerEntity('12:00', true);
entity.meridiem = 'PM';
entity.toggleMeridiem();

expect(entity.toString()).toBe('12:00');
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ import type { TimePickerMeridiem } from '../TimePicker/TimePicker.type';
export type TimePickerFormatProps = BoxProps & {};

export type TimePickerFormatPropsInternal = {
/** Internal */
/** @Internal */
getI18nText: ReturnType<typeof useI18n>;
/** Internal */
/** @Internal */
size: 'm' | 'l';
/** Internal */
/** @Internal */
disabled: boolean;
/** Internal */
/** @Internal */
meridiem: TimePickerMeridiem;
/** Internal */
/** @Internal */
onClick: (event: React.SyntheticEvent) => void;
};
4 changes: 2 additions & 2 deletions semcore/time-picker/src/component/TimePicker/TimePicker.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createComponent, Component, sstyled, Root } from '@semcore/core';
import propsObserver from '@semcore/core/lib/decorators/propsObserver';

Check failure on line 2 in semcore/time-picker/src/component/TimePicker/TimePicker.tsx

View workflow job for this annotation

GitHub Actions / static-lint

Cannot find module '@semcore/core/lib/decorators/propsObserver' or its corresponding type declarations.
import reactive from '@semcore/core/lib/decorators/reactive';
import trackPropsChanges from '@semcore/core/lib/decorators/trackPropsChanges';
import i18nEnhance from '@semcore/core/lib/utils/enhances/i18nEnhance';
import { Box } from '@semcore/flex-box';
import Input from '@semcore/input';
Expand All @@ -18,7 +18,7 @@
import Format from '../PickerFormat/PickerFormat';
import { Hours, Minutes } from '../PickerInput/PickerInput';

@trackPropsChanges(['value'])
@propsObserver(['value'])
class TimePickerRoot extends Component<TimePickerProps, {}, {}, typeof TimePickerRoot.enhance> {
static displayName = 'TimePicker';
static style = style;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ export type TimePickerProps = Omit<InputProps, 'size'> & {
};

export type TimePickerSeparatorProps = {
/** Internal */
/** @Internal */
hoursInputRef: React.RefObject<HTMLElement>;
/** Internal */
/** @Internal */
disabled?: boolean;
};

Expand Down
Loading
Loading