Skip to content

Commit c7704bf

Browse files
hastinbeclaudejasonvarga
authored
[6.x] Support decimal values in Range fieldtype (#13096)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Jason Varga <jason@pixelfear.com>
1 parent 229f741 commit c7704bf

3 files changed

Lines changed: 224 additions & 7 deletions

File tree

src/Fieldtypes/Range.php

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,27 @@ protected function configFieldItems(): array
1818
'min' => [
1919
'display' => __('Min'),
2020
'instructions' => __('statamic::fieldtypes.range.config.min'),
21-
'type' => 'integer',
21+
'type' => 'text',
22+
'input_type' => 'number',
23+
'validate' => ['numeric'],
2224
'default' => 0,
2325
'width' => 50,
2426
],
2527
'max' => [
2628
'display' => __('Max'),
2729
'instructions' => __('statamic::fieldtypes.range.config.max'),
28-
'type' => 'integer',
30+
'type' => 'text',
31+
'input_type' => 'number',
32+
'validate' => ['numeric'],
2933
'default' => 100,
3034
'width' => 50,
3135
],
3236
'step' => [
3337
'display' => __('Step'),
3438
'instructions' => __('statamic::fieldtypes.range.config.step'),
35-
'type' => 'integer',
39+
'type' => 'text',
40+
'input_type' => 'number',
41+
'validate' => ['numeric'],
3642
'default' => 1,
3743
'width' => 50,
3844
],
@@ -72,11 +78,33 @@ protected function configFieldItems(): array
7278

7379
public function process($data)
7480
{
81+
if ($this->usesDecimals()) {
82+
return (float) $data;
83+
}
84+
7585
return (int) $data;
7686
}
7787

88+
protected function usesDecimals(): bool
89+
{
90+
$step = $this->config('step', 1);
91+
$min = $this->config('min', 0);
92+
$max = $this->config('max', 100);
93+
94+
return $this->isDecimal($step) || $this->isDecimal($min) || $this->isDecimal($max);
95+
}
96+
97+
protected function isDecimal($value): bool
98+
{
99+
if (! is_numeric($value)) {
100+
return false;
101+
}
102+
103+
return floor((float) $value) != (float) $value;
104+
}
105+
78106
public function toGqlType()
79107
{
80-
return GraphQL::int();
108+
return $this->usesDecimals() ? GraphQL::float() : GraphQL::int();
81109
}
82110
}

tests/Feature/GraphQL/Fieldtypes/RangeFieldtypeTest.php

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,16 @@
99
class RangeFieldtypeTest extends FieldtypeTestCase
1010
{
1111
#[Test]
12-
public function it_gets_an_integer()
12+
public function it_processes_integer_values_with_integer_config()
1313
{
1414
$this->createEntryWithFields([
1515
'filled' => [
1616
'value' => '7',
17-
'field' => ['type' => 'range'],
17+
'field' => ['type' => 'range', 'min' => 0, 'max' => 100, 'step' => 1],
1818
],
1919
'undefined' => [
2020
'value' => null,
21-
'field' => ['type' => 'range'],
21+
'field' => ['type' => 'range', 'min' => 0, 'max' => 100, 'step' => 1],
2222
],
2323
]);
2424

@@ -27,4 +27,24 @@ public function it_gets_an_integer()
2727
'undefined' => null,
2828
]);
2929
}
30+
31+
#[Test]
32+
public function it_processes_decimal_values_with_decimal_config()
33+
{
34+
$this->createEntryWithFields([
35+
'filled' => [
36+
'value' => '7.5',
37+
'field' => ['type' => 'range', 'min' => 0, 'max' => 100, 'step' => 0.1],
38+
],
39+
'another' => [
40+
'value' => '3.14',
41+
'field' => ['type' => 'range', 'min' => 0, 'max' => 10, 'step' => 0.01],
42+
],
43+
]);
44+
45+
$this->assertGqlEntryHas('filled, another', [
46+
'filled' => 7.5,
47+
'another' => 3.14,
48+
]);
49+
}
3050
}
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
<?php
2+
3+
namespace Tests\Fieldtypes;
4+
5+
use PHPUnit\Framework\Attributes\Test;
6+
use Statamic\Fields\Field;
7+
use Statamic\Fieldtypes\Range;
8+
use Tests\TestCase;
9+
10+
class RangeFieldtypeTest extends TestCase
11+
{
12+
#[Test]
13+
public function it_processes_as_integer_with_integer_config()
14+
{
15+
$fieldtype = (new Range())->setField(new Field('test', [
16+
'type' => 'range',
17+
'min' => 0,
18+
'max' => 100,
19+
'step' => 1,
20+
]));
21+
22+
$result = $fieldtype->process('7');
23+
24+
$this->assertIsInt($result);
25+
$this->assertEquals(7, $result);
26+
}
27+
28+
#[Test]
29+
public function it_processes_as_float_with_decimal_step()
30+
{
31+
$fieldtype = (new Range())->setField(new Field('test', [
32+
'type' => 'range',
33+
'min' => 0,
34+
'max' => 100,
35+
'step' => 0.1,
36+
]));
37+
38+
$result = $fieldtype->process('7.5');
39+
40+
$this->assertIsFloat($result);
41+
$this->assertEquals(7.5, $result);
42+
}
43+
44+
#[Test]
45+
public function it_processes_as_float_with_decimal_min()
46+
{
47+
$fieldtype = (new Range())->setField(new Field('test', [
48+
'type' => 'range',
49+
'min' => 0.5,
50+
'max' => 100,
51+
'step' => 1,
52+
]));
53+
54+
$result = $fieldtype->process('7');
55+
56+
$this->assertIsFloat($result);
57+
$this->assertEquals(7.0, $result);
58+
}
59+
60+
#[Test]
61+
public function it_processes_as_float_with_decimal_max()
62+
{
63+
$fieldtype = (new Range())->setField(new Field('test', [
64+
'type' => 'range',
65+
'min' => 0,
66+
'max' => 99.9,
67+
'step' => 1,
68+
]));
69+
70+
$result = $fieldtype->process('7');
71+
72+
$this->assertIsFloat($result);
73+
$this->assertEquals(7.0, $result);
74+
}
75+
76+
#[Test]
77+
public function it_processes_zero_as_integer_with_integer_config()
78+
{
79+
$fieldtype = (new Range())->setField(new Field('test', [
80+
'type' => 'range',
81+
'min' => 0,
82+
'max' => 100,
83+
'step' => 1,
84+
]));
85+
86+
$result = $fieldtype->process('0');
87+
88+
$this->assertIsInt($result);
89+
$this->assertEquals(0, $result);
90+
}
91+
92+
#[Test]
93+
public function it_processes_negative_values_with_decimal_step()
94+
{
95+
$fieldtype = (new Range())->setField(new Field('test', [
96+
'type' => 'range',
97+
'min' => -10,
98+
'max' => 10,
99+
'step' => 0.5,
100+
]));
101+
102+
$result = $fieldtype->process('-2.5');
103+
104+
$this->assertIsFloat($result);
105+
$this->assertEquals(-2.5, $result);
106+
}
107+
108+
#[Test]
109+
public function it_processes_as_float_with_string_decimal_config()
110+
{
111+
$fieldtype = (new Range())->setField(new Field('test', [
112+
'type' => 'range',
113+
'min' => '0',
114+
'max' => '100',
115+
'step' => '0.5',
116+
]));
117+
118+
$result = $fieldtype->process('7.5');
119+
120+
$this->assertIsFloat($result);
121+
$this->assertEquals(7.5, $result);
122+
}
123+
124+
#[Test]
125+
public function it_processes_as_integer_with_string_integer_config()
126+
{
127+
$fieldtype = (new Range())->setField(new Field('test', [
128+
'type' => 'range',
129+
'min' => '0',
130+
'max' => '100',
131+
'step' => '1',
132+
]));
133+
134+
$result = $fieldtype->process('7');
135+
136+
$this->assertIsInt($result);
137+
$this->assertEquals(7, $result);
138+
}
139+
140+
#[Test]
141+
public function it_returns_int_graphql_type_with_integer_config()
142+
{
143+
$fieldtype = (new Range())->setField(new Field('test', [
144+
'type' => 'range',
145+
'min' => 0,
146+
'max' => 100,
147+
'step' => 1,
148+
]));
149+
150+
$type = $fieldtype->toGqlType();
151+
152+
$this->assertEquals('Int', $type->name);
153+
}
154+
155+
#[Test]
156+
public function it_returns_float_graphql_type_with_decimal_config()
157+
{
158+
$fieldtype = (new Range())->setField(new Field('test', [
159+
'type' => 'range',
160+
'min' => 0,
161+
'max' => 100,
162+
'step' => 0.1,
163+
]));
164+
165+
$type = $fieldtype->toGqlType();
166+
167+
$this->assertEquals('Float', $type->name);
168+
}
169+
}

0 commit comments

Comments
 (0)