Welcome 👋
Next to a number field sit two small arrows, ▲ and ▼: the SpinUpDown trigger. It builds both buttons, and on a click it decides which one was hit from the clicked element's CSS classes. That routing is the whole trick, it is short, and nothing tests it.
This is a good first issue for Hacktoberfest 2026. It needs Node.js and a clone of this repository: no Docker, no AI setup.
[!IMPORTANT]
Reserved for first-time Neo.mjs contributors. The maintainer team will not claim, edit, or implement this issue through 2026-10-31. Comment here to claim it, and ask anything on this issue.
Set up once: follow Set up the engine from a clone, including npm run bundle-browser-deps: without it the unit suite selects zero tests.
Where things live
- Source:
src/form/field/trigger/SpinUpDown.mjs (a NumberField reaches it with field.getTrigger('spinupdown'))
- New test file:
test/playwright/unit/form/field/trigger/SpinUpDown.spec.mjs
- Pattern to copy:
test/playwright/unit/form/field/InputWidth.spec.mjs (creating a field in a unit test)
- Tips: a
NumberField needs one main-thread stub while your tests run. Install it in test.beforeAll, only if it is missing, and remove it in test.afterAll, exactly as test/playwright/unit/apps/colors/ViewportController.spec.mjs does in its test.beforeAll: const main = Neo.ns('Neo.main', true); if (!main.DomEvents) { main.DomEvents = {registerDisabledInputChars() {}, unregisterDisabledInputChars() {}}; /* delete it again in afterAll */ }. Do not put it at the top of the file: another spec imports the real DomEvents, and npm run test-unit then stops with Namespace collision in unitTestMode for Neo.main.DomEvents. To see which method a click reaches, replace the field's onSpinButtonUpClick / onSpinButtonDownClick with functions that record their calls.
What to cover
| Case |
Expected |
a NumberField's spin trigger |
vdom.cn has two buttons with the classes neo-spin-button neo-up fa fa-chevron-up and neo-spin-button neo-down fa fa-chevron-down |
custom spinButtonUpIconCls / spinButtonDownIconCls |
their classes are split into the buttons' class lists |
onTriggerClick({path: [{cls: ['neo-spin-button', 'neo-up']}]}) |
calls the field's onSpinButtonUpClick() |
the same with neo-down |
calls onSpinButtonDownClick() |
| a click whose target has neither class |
calls neither |
toJSON() |
includes spinButtonUpIconCls and spinButtonDownIconCls |
Done when
Out of scope
What the number field does with a spin (its step and min/max logic), and the single-button SpinUp / SpinDown triggers.
While you wait for review
You just pinned one small piece of a multi-threaded application engine: in Neo.mjs your app's code runs inside a web worker, and the main thread only paints. Two things worth ten minutes:
- See the part nobody expects. Run
npm run build-themes -- -n -e dev -t all and npm run server-start, open apps/workstation/, and drag a tab out of the browser window. It becomes its own OS window, still running, with the same component instances and the same worker. Benefits: Introduction explains why that is possible.
- Your coding agent can go much further here. The skills this repository's AI maintainers work with are published on npm as
neo-agent-skills, and they need no Knowledge Base and no Memory Core. With Claude or GPT beside you, the help wanted issues are within reach after this one.
Maintainer notes
Curated under #18985 (Hacktoberfest 2026). Found work: no spec exists under test/playwright/unit/form/field/trigger/ on dev (2026-09-23). Rows 1, 3, 4, 5 and 6 were run in the unit environment: the two class lists, ['up', 'down'] for the two clicks, nothing for the third, and both icon classes in toJSON().
Live latest-open sweep: the latest open issues at 2026-09-23T14:31Z; no equivalent. A2A: no claim on this scope. Memory Core: no prior decision.
Origin Session ID: 3be453e4-8b04-4865-be62-4cff34f4e0c6
Welcome 👋
Next to a number field sit two small arrows, ▲ and ▼: the
SpinUpDowntrigger. It builds both buttons, and on a click it decides which one was hit from the clicked element's CSS classes. That routing is the whole trick, it is short, and nothing tests it.This is a good first issue for Hacktoberfest 2026. It needs Node.js and a clone of this repository: no Docker, no AI setup.
Set up once: follow Set up the engine from a clone, including
npm run bundle-browser-deps: without it the unit suite selects zero tests.Where things live
src/form/field/trigger/SpinUpDown.mjs(aNumberFieldreaches it withfield.getTrigger('spinupdown'))test/playwright/unit/form/field/trigger/SpinUpDown.spec.mjstest/playwright/unit/form/field/InputWidth.spec.mjs(creating a field in a unit test)NumberFieldneeds one main-thread stub while your tests run. Install it intest.beforeAll, only if it is missing, and remove it intest.afterAll, exactly astest/playwright/unit/apps/colors/ViewportController.spec.mjsdoes in itstest.beforeAll:const main = Neo.ns('Neo.main', true); if (!main.DomEvents) { main.DomEvents = {registerDisabledInputChars() {}, unregisterDisabledInputChars() {}}; /* delete it again in afterAll */ }. Do not put it at the top of the file: another spec imports the realDomEvents, andnpm run test-unitthen stops withNamespace collision in unitTestMode for Neo.main.DomEvents. To see which method a click reaches, replace the field'sonSpinButtonUpClick/onSpinButtonDownClickwith functions that record their calls.What to cover
NumberField's spin triggervdom.cnhas two buttons with the classesneo-spin-button neo-up fa fa-chevron-upandneo-spin-button neo-down fa fa-chevron-downspinButtonUpIconCls/spinButtonDownIconClsonTriggerClick({path: [{cls: ['neo-spin-button', 'neo-up']}]})onSpinButtonUpClick()neo-downonSpinButtonDownClick()toJSON()spinButtonUpIconClsandspinButtonDownIconClsDone when
test/playwright/unit/form/field/trigger/SpinUpDown.spec.mjsexists, following the pattern file's setup.test(...)per table row.src/.npm run test-unit -- test/playwright/unit/form/field/trigger/SpinUpDown.spec.mjspasses.npm run test-unitpasses.Out of scope
What the number field does with a spin (its step and min/max logic), and the single-button
SpinUp/SpinDowntriggers.While you wait for review
You just pinned one small piece of a multi-threaded application engine: in Neo.mjs your app's code runs inside a web worker, and the main thread only paints. Two things worth ten minutes:
npm run build-themes -- -n -e dev -t allandnpm run server-start, openapps/workstation/, and drag a tab out of the browser window. It becomes its own OS window, still running, with the same component instances and the same worker. Benefits: Introduction explains why that is possible.neo-agent-skills, and they need no Knowledge Base and no Memory Core. With Claude or GPT beside you, thehelp wantedissues are within reach after this one.Maintainer notes
Curated under #18985 (Hacktoberfest 2026). Found work: no spec exists under
test/playwright/unit/form/field/trigger/ondev(2026-09-23). Rows 1, 3, 4, 5 and 6 were run in the unit environment: the two class lists,['up', 'down']for the two clicks, nothing for the third, and both icon classes intoJSON(). Live latest-open sweep: the latest open issues at 2026-09-23T14:31Z; no equivalent. A2A: no claim on this scope. Memory Core: no prior decision.Origin Session ID: 3be453e4-8b04-4865-be62-4cff34f4e0c6