From 545257ba76bf73c00c33da7b13df8c3c65c21e76 Mon Sep 17 00:00:00 2001 From: Hayk Kirakosyan Date: Wed, 29 Oct 2025 18:37:19 +0400 Subject: [PATCH 01/27] feat(CounterField): component boilerplate --- .../molecules/CounterField/CounterField.scss | 5 ++++ .../CounterField/CounterField.stories.tsx | 24 ++++++++++++++++++ .../CounterField/CounterField.test.tsx | 25 +++++++++++++++++++ .../molecules/CounterField/CounterField.tsx | 23 +++++++++++++++++ .../molecules/CounterField/index.tsx | 1 + src/index.ts | 1 + 6 files changed, 79 insertions(+) create mode 100644 src/components/molecules/CounterField/CounterField.scss create mode 100644 src/components/molecules/CounterField/CounterField.stories.tsx create mode 100644 src/components/molecules/CounterField/CounterField.test.tsx create mode 100644 src/components/molecules/CounterField/CounterField.tsx create mode 100644 src/components/molecules/CounterField/index.tsx diff --git a/src/components/molecules/CounterField/CounterField.scss b/src/components/molecules/CounterField/CounterField.scss new file mode 100644 index 00000000..ce313489 --- /dev/null +++ b/src/components/molecules/CounterField/CounterField.scss @@ -0,0 +1,5 @@ +.counterField { + // Your styles here + color: var(--guit-ref-color-magenta-500base); + font-size: var(--guit-sem-font-caption-large-medium-font-size); +} diff --git a/src/components/molecules/CounterField/CounterField.stories.tsx b/src/components/molecules/CounterField/CounterField.stories.tsx new file mode 100644 index 00000000..ff1f7525 --- /dev/null +++ b/src/components/molecules/CounterField/CounterField.stories.tsx @@ -0,0 +1,24 @@ +import { Meta, StoryObj } from "@storybook/react"; + +// Helpers +import { args, propCategory } from "../../../../stories/assets/storybook.globals"; +// Components +import CounterField, { ICounterFieldProps } from "./index"; + +const meta: Meta = { + title: "Molecules/CounterField", + component: CounterField, + argTypes: { + className: args({ control: "false", ...propCategory.appearance }) + // fill CounterField component argTypes + }, + args: { + // fill CounterField component args + } +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/src/components/molecules/CounterField/CounterField.test.tsx b/src/components/molecules/CounterField/CounterField.test.tsx new file mode 100644 index 00000000..1bd43eeb --- /dev/null +++ b/src/components/molecules/CounterField/CounterField.test.tsx @@ -0,0 +1,25 @@ +import React from "react"; +import { mount, ReactWrapper } from "enzyme"; + +// Components +import CounterField, { ICounterFieldProps } from "./index"; + +describe("CounterField ", () => { + let setup: ReactWrapper; + beforeEach(() => { + setup = mount(); + }); + + it("renders without crashing", () => { + expect(setup.exists()).toBeTruthy(); + }); + + it("renders className prop correctly", () => { + const className = "test-class"; + const wrapper = setup.setProps({ className }); + + expect(wrapper.hasClass(className)).toBeTruthy(); + }); + + // Your tests here +}); diff --git a/src/components/molecules/CounterField/CounterField.tsx b/src/components/molecules/CounterField/CounterField.tsx new file mode 100644 index 00000000..0ec0e7c0 --- /dev/null +++ b/src/components/molecules/CounterField/CounterField.tsx @@ -0,0 +1,23 @@ +import React, { FC } from "react"; +import classNames from "classnames"; + +// Styles +import "./CounterField.scss"; + +interface ICounterFieldProps { + /** + * Additional class for the parent element. + * This prop should be used to set placement properties for the element relative to its parent using BEM conventions. + */ + className?: string; + // fill CounterField component props interface +} + +/** + * CounterField + */ +const CounterField: FC = ({ className }) => { + return
CounterField
; +}; + +export { ICounterFieldProps, CounterField as default }; diff --git a/src/components/molecules/CounterField/index.tsx b/src/components/molecules/CounterField/index.tsx new file mode 100644 index 00000000..14350e2a --- /dev/null +++ b/src/components/molecules/CounterField/index.tsx @@ -0,0 +1 @@ +export { ICounterFieldProps, default } from "./CounterField"; diff --git a/src/index.ts b/src/index.ts index 388ca0ac..ae046d8d 100755 --- a/src/index.ts +++ b/src/index.ts @@ -55,6 +55,7 @@ export { default as Banner, IBannerProps } from "./components/molecules/Banner"; export { default as TagGroup, ITagGroupProps } from "./components/molecules/TagGroup"; export { default as DataCard, IDataCardProps } from "./components/molecules/DataCard"; export { default as RadioGroup, IRadioGroupProps, IRadioOption } from "./components/molecules/RadioGroup"; +export { default as CounterField } from "./components/molecules/CounterField"; // Organisms export { default as GlobalHeader, IGlobalHeaderProps, IAction, IProducts } from "./components/organisms/GlobalHeader"; From 99fccd68c513d559297b23e5ea82bcc74a0badbb Mon Sep 17 00:00:00 2001 From: Hayk Kirakosyan Date: Fri, 31 Oct 2025 10:06:39 +0400 Subject: [PATCH 02/27] feat: adding some UI parts --- .../molecules/CounterField/CounterField.tsx | 148 +++++++++++++++++- 1 file changed, 144 insertions(+), 4 deletions(-) diff --git a/src/components/molecules/CounterField/CounterField.tsx b/src/components/molecules/CounterField/CounterField.tsx index 0ec0e7c0..b9e48c5d 100644 --- a/src/components/molecules/CounterField/CounterField.tsx +++ b/src/components/molecules/CounterField/CounterField.tsx @@ -1,6 +1,12 @@ -import React, { FC } from "react"; +import React, { ChangeEvent, FC, FocusEvent } from "react"; import classNames from "classnames"; +import HelperText from "@components/atoms/HelperText"; +import Label from "@components/atoms/Label"; +import Button from "@components/atoms/Button"; +import TextField from "@components/molecules/TextField"; +import { Plus, Minus } from "@geneui/icons"; + // Styles import "./CounterField.scss"; @@ -10,14 +16,148 @@ interface ICounterFieldProps { * This prop should be used to set placement properties for the element relative to its parent using BEM conventions. */ className?: string; - // fill CounterField component props interface + /** + * The value of the counter (controlled). + * This is what will be returned in onChange and stored as the selected value. + */ + value?: number; + /** + * The initial value of the counter (uncontrolled). + */ + defaultValue?: number; + /** + * Disables the counter field, preventing it from being interacted with. + */ + disabled?: boolean; + /** + * Makes the counter field read-only, preventing manual input changes. + */ + readOnly?: boolean; + + // BOUNDARIES & STEP + /** + * The minimum allowed value. + */ + min?: number; + /** + * The amount by which the value increases or decreases. + */ + step?: number; + + // APPEARANCE & LAYOUT + /** + * Size of the component. Possible values: `small | medium | large` + */ + size?: 'small' | 'medium' | 'large'; + /** + * The status/validation state of the component. Possible values: `rest | warning | error` + */ + status?: "rest" | "warning" | "error"; + /** + * Text alignment of the value in the input field. Possible values: `left | center | right` + */ + alignment?: 'left' | 'right'; + /** + * The main label for the counter field. + */ + label?: string; + /** + * Additional descriptive text that appears alongside the `label`, typically displayed as a tooltip. + */ + infoText?: string; + /** + * Helper text that appears below the counter field. + */ + helperText?: string; + /** + * Indicates that the field is required. + */ + required?: boolean; + + // ACTIONS + /** + * Fires when the user changes the counter value (via buttons or input). + */ + onChange?: (value: number, event: ChangeEvent) => void; + /** + * Fires when the input field loses focus. + */ + onBlur?: (event: FocusEvent) => void; + /** + * Fires when the input field receives focus. + */ + onFocus?: (event: FocusEvent) => void; } /** * CounterField */ -const CounterField: FC = ({ className }) => { - return
CounterField
; +const CounterField: FC = ({ label, + infoText, + required, + disabled, + readOnly, + helperText, + status = "rest", + value, + defaultValue, + onChange, + onBlur, + onFocus, + min, + step, + size, + alignment, + className }) => { + return
{label && ( +
; }; export { ICounterFieldProps, CounterField as default }; From 9804c293f3810db1bbba2cec28ef976cdec9ddef Mon Sep 17 00:00:00 2001 From: Hayk Kirakosyan Date: Mon, 3 Nov 2025 16:10:57 +0400 Subject: [PATCH 03/27] feat: working on component stories --- .../molecules/CounterField/CounterField.scss | 1 + .../CounterField/CounterField.stories.tsx | 63 +++++++- .../molecules/CounterField/CounterField.tsx | 149 ++++++++++-------- 3 files changed, 140 insertions(+), 73 deletions(-) diff --git a/src/components/molecules/CounterField/CounterField.scss b/src/components/molecules/CounterField/CounterField.scss index ce313489..d9c48a62 100644 --- a/src/components/molecules/CounterField/CounterField.scss +++ b/src/components/molecules/CounterField/CounterField.scss @@ -2,4 +2,5 @@ // Your styles here color: var(--guit-ref-color-magenta-500base); font-size: var(--guit-sem-font-caption-large-medium-font-size); + background: blue; } diff --git a/src/components/molecules/CounterField/CounterField.stories.tsx b/src/components/molecules/CounterField/CounterField.stories.tsx index ff1f7525..625e75e9 100644 --- a/src/components/molecules/CounterField/CounterField.stories.tsx +++ b/src/components/molecules/CounterField/CounterField.stories.tsx @@ -1,3 +1,4 @@ +import React, { FC } from "react"; import { Meta, StoryObj } from "@storybook/react"; // Helpers @@ -7,13 +8,59 @@ import CounterField, { ICounterFieldProps } from "./index"; const meta: Meta = { title: "Molecules/CounterField", + component: CounterField, + argTypes: { - className: args({ control: "false", ...propCategory.appearance }) - // fill CounterField component argTypes + value: args({ control: "false", ...propCategory.states }), + + defaultValue: args({ control: "number", ...propCategory.states }), + + disabled: args({ control: "boolean", ...propCategory.states }), + + readOnly: args({ control: "boolean", ...propCategory.states }), + + min: args({ control: "number", ...propCategory.validation }), + + step: args({ control: "number", defaultValue: 1, ...propCategory.validation }), + + size: args({ control: "select", ...propCategory.appearance }), + + status: args({ control: "select", ...propCategory.appearance }), + + className: args({ control: "false", ...propCategory.appearance }), + + label: args({ control: "text", ...propCategory.content }), + + infoText: args({ control: "text", ...propCategory.content }), + + helperText: args({ control: "text", ...propCategory.content }), + + required: args({ control: "boolean", ...propCategory.states }), + + onChange: args({ control: "false", ...propCategory.action }), + + onBlur: args({ control: "false", ...propCategory.action }), + + onFocus: args({ control: "false", ...propCategory.action }) }, + args: { - // fill CounterField component args + defaultValue: 0, + + min: 0, + + step: 1, + + size: "medium", + + status: "rest", + + label: "Quantity", + + helperText: "Helper text", + + required: false } }; @@ -21,4 +68,12 @@ export default meta; type Story = StoryObj; -export const Default: Story = {}; +const Template: FC = (props) => ( +
+ +
+); + +export const Default: Story = { + render: (props) =>