Skip to content

Commit 5512179

Browse files
committed
[5.x] Support decimal values in Range fieldtype
The Range fieldtype now properly supports decimal values (e.g., 0.5, 3.14) to align with the HTML range input spec, which allows the step attribute to accept decimal values. Changes: - Config fields (min, max, step) now accept decimal values - process() method returns int or float based on field configuration - If min, max, or step contains decimals, returns float - If all config values are integers, returns int (backwards compatible) - GraphQL type dynamically determined based on field configuration This maintains full backwards compatibility: existing Range fields with integer configurations continue to return integers and use Int GraphQL type. Only fields explicitly configured with decimal values will use Float type.
1 parent 30d3df4 commit 5512179

File tree

3 files changed

+189
-7
lines changed

3 files changed

+189
-7
lines changed

src/Fieldtypes/Range.php

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,22 @@ 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',
2223
'default' => 0,
2324
],
2425
'max' => [
2526
'display' => __('Max'),
2627
'instructions' => __('statamic::fieldtypes.range.config.max'),
27-
'type' => 'integer',
28+
'type' => 'text',
29+
'input_type' => 'number',
2830
'default' => 100,
2931
],
3032
'step' => [
3133
'display' => __('Step'),
3234
'instructions' => __('statamic::fieldtypes.range.config.step'),
33-
'type' => 'integer',
35+
'type' => 'text',
36+
'input_type' => 'number',
3437
'default' => 1,
3538
],
3639
'default' => [
@@ -62,11 +65,33 @@ protected function configFieldItems(): array
6265

6366
public function process($data)
6467
{
68+
if ($this->usesDecimals()) {
69+
return (float) $data;
70+
}
71+
6572
return (int) $data;
6673
}
6774

75+
protected function usesDecimals(): bool
76+
{
77+
$step = $this->config('step', 1);
78+
$min = $this->config('min', 0);
79+
$max = $this->config('max', 100);
80+
81+
return $this->isDecimal($step) || $this->isDecimal($min) || $this->isDecimal($max);
82+
}
83+
84+
protected function isDecimal($value): bool
85+
{
86+
if (! is_numeric($value)) {
87+
return false;
88+
}
89+
90+
return floor((float) $value) != (float) $value;
91+
}
92+
6893
public function toGqlType()
6994
{
70-
return GraphQL::int();
95+
return $this->usesDecimals() ? GraphQL::float() : GraphQL::int();
7196
}
7297
}

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: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
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_returns_int_graphql_type_with_integer_config()
110+
{
111+
$fieldtype = (new Range())->setField(new Field('test', [
112+
'type' => 'range',
113+
'min' => 0,
114+
'max' => 100,
115+
'step' => 1,
116+
]));
117+
118+
$type = $fieldtype->toGqlType();
119+
120+
$this->assertEquals('Int', $type->name);
121+
}
122+
123+
#[Test]
124+
public function it_returns_float_graphql_type_with_decimal_config()
125+
{
126+
$fieldtype = (new Range())->setField(new Field('test', [
127+
'type' => 'range',
128+
'min' => 0,
129+
'max' => 100,
130+
'step' => 0.1,
131+
]));
132+
133+
$type = $fieldtype->toGqlType();
134+
135+
$this->assertEquals('Float', $type->name);
136+
}
137+
}

0 commit comments

Comments
 (0)