*/
public array $price = [100, 400];
public function render(): string
{
return <<<'BLADE'
BLADE;
}
};
Livewire::test($component)
->assertSeeHtml('value="70"')
->assertSeeHtml('style="left: calc(70% + 0px)"')
->assertSeeHtml('value="100"')
->assertSeeHtml('value="400"');
});
it('replaces the hint with the validation message and marks the input invalid', function () {
$errors = (new ViewErrorBag)->put('default', new MessageBag(['volume' => ['Too loud.'], 'price.1' => ['Too expensive.']]));
$this->withViewErrors([]);
view()->share('errors', $errors);
expect((string) $this->blade(''))
->toContain('Too loud.')
->toContain('')
->toContain('aria-invalid="true"')
->toContain('aria-describedby="volume-hint"')
->not->toContain('Quietly');
expect((string) $this->blade(''))
->toContain('Too expensive.')
->not->toContain('Per night');
expect((string) $this->blade(''))
->toMatch('/data-md-slider-hint>Centre is even<\/p>/')
->not->toContain('aria-invalid');
});
it('sizes the track and the handle by Expressive\'s tokens', function (string $size, string $track, string $handle, string $across) {
expect((string) $this->blade('', ['size' => $size]))
->toContain("data-md-size=\"{$size}\"");
$css = (string) file_get_contents(__DIR__.'/../../../resources/css/components/slider.css');
$block = $size === 'xs'
? '/\[data-md-slider\] \{[^}]*/'
: '/\[data-md-slider\]\[data-md-size=\''.$size.'\'\] \{[^}]*/';
preg_match($block, $css, $rule);
foreach (['--slider-across' => $across, '--slider-track' => $track, '--slider-handle' => $handle] as $property => $value) {
if ($size === 'sm' && $property !== '--slider-track') {
// sm keeps xs's handle and height.
continue;
}
expect($rule[0])->toContain("{$property}: {$value};");
}
// A focused handle is 6px shorter, so its ring stays clear of the label.
expect($css)->toContain('height: calc(var(--slider-handle) - 6px);');
})->with([
'xs' => ['xs', '16px', '44px', '48px'],
'sm' => ['sm', '24px', '44px', '48px'],
'md' => ['md', '40px', '52px', '52px'],
'lg' => ['lg', '56px', '68px', '68px'],
'xl' => ['xl', '96px', '108px', '108px'],
]);
it('insets an icon in the track from md, but not on a small, range or centred slider', function () {
expect((string) $this->blade(''))
->toMatch('/data-md-slider-icon\s+data-md-active/')
->toContain('--md-icon-size: 24px');
expect((string) $this->blade(''))
->toMatch('/data-md-slider-icon\s+style=/')
->toContain('--md-icon-size: 32px');
foreach (['', '', ''] as $template) {
expect((string) $this->blade($template))->not->toContain('data-md-slider-icon');
}
});
it('fills a centred slider from the middle', function () {
expect((string) $this->blade(''))
->toContain('data-md-centered')
->toMatch('/data-md-slider-segment="start"\s+style="left: calc\(0% \+ 0px\); width: calc\(50% - 6px\)/')
->toMatch('/data-md-slider-segment="active"\s+style="left: calc\(50% \+ 0px\); width: calc\(25% - 8px\)/')
->toMatch('/data-md-slider-segment="end"\s+style="left: calc\(75% \+ 8px\)/');
});
it('marks every step when asked, and no more than 200 of them', function () {
$html = (string) $this->blade('');
expect(substr_count($html, 'data-md-slider-tick="'))->toBe(11)
->and($html)->toContain('data-md-slider-tick="0.5"')
->and($html)->toMatch('/data-md-slider-tick="0.2"\s+data-md-active/')
->and((string) file_get_contents(__DIR__.'/../../../resources/css/components/slider.css'))
->toMatch('/\[data-md-slider-tick\]\[data-md-active\] \{\s*background-color: var\(--slider-on-active\);/');
expect((string) $this->blade(''))->not->toContain('data-md-slider-tick=')
->and((string) $this->blade(''))->not->toContain('data-md-slider-tick=')
->and((string) $this->blade(''))->not->toContain('data-md-slider-tick=');
});
it('shows the value label on drag, always, or never', function () {
expect((string) $this->blade(''))->toContain('data-md-value-label="drag"')
->and((string) file_get_contents(__DIR__.'/../../../resources/css/components/slider.css'))
->toMatch("/\\[data-md-slider\\]\\[data-md-value-label='drag'\\] \\[data-md-slider-value\\] \\{\\s*opacity: 0;\\s*scale: 0.75;/")
->toMatch("/\\[data-md-slider\\]\\[data-md-value-label='always'\\] \\[data-md-slider-control\\] \\{\\s*margin-top: var\\(--md-sys-measurement-space600\\);/");
expect((string) $this->blade(''))
->toContain('data-md-value-label="always"')
->toContain('30');
expect((string) $this->blade(''))->not->toContain('data-md-slider-value');
});
it('stands a slider up on request, turning the drawing a quarter', function () {
$html = (string) $this->blade('');
expect($html)
->toContain('data-md-orientation="vertical"')
->toContain('aria-orientation="vertical"')
->toContain('class="app-volume-slider"');
$css = (string) file_get_contents(__DIR__.'/../../../resources/css/components/slider.css');
expect($css)
// The slider is as wide as a horizontal one is tall, and as long as its wrapper.
->toMatch("/\\[data-md-slider\\]\\[data-md-orientation='vertical'\\] \\[data-md-slider-control\\] \\{[^}]*width: var\\(--slider-across\\);[^}]*height: 192px;[^}]*container-type: size;/")
->toMatch("/\\[data-md-slider\\]\\[data-md-orientation='vertical'\\] \\[data-md-slider-drawing\\] \\{[^}]*width: calc\\(100cqh - var\\(--md-sys-measurement-space50\\)\\);[^}]*height: 100cqw;[^}]*rotate: -90deg;/")
// The value label is turned back, so it lands beside the handle and reads across.
->toMatch("/\\[data-md-slider\\]\\[data-md-orientation='vertical'\\] \\[data-md-slider-value\\] \\{[^}]*rotate: 90deg;/")
->toMatch("/\\[data-md-slider\\]\\[data-md-value-label='always'\\]\\[data-md-orientation='vertical'\\] \\[data-md-slider-control\\] \\{[^}]*margin-inline-start: var\\(--md-sys-measurement-space600\\);/");
});
it('keeps a range slider horizontal, as M3 asks', function () {
expect((string) $this->blade(''))
->not->toContain('data-md-orientation')
->not->toContain('aria-orientation');
});
it('draws in the colour and its container, and greys out when disabled', function (string $color, string $active, string $inactive) {
expect((string) $this->blade('', ['color' => $color]))
->toContain('data-md-color="'.($color === 'pink' ? 'primary' : $color).'"')
->toMatch('/]*\sdisabled[\s>]/');
$css = (string) file_get_contents(__DIR__.'/../../../resources/css/components/slider.css');
if ($color === 'primary' || $color === 'pink') {
expect($css)->toMatch("/\\[data-md-slider\\] \\{[^}]*--slider-active: var\\(--md-sys-color-{$active}\\);[^}]*--slider-inactive: var\\(--md-sys-color-{$inactive}\\);/");
} else {
expect($css)->toMatch("/\\[data-md-slider\\]\\[data-md-color='{$color}'\\] \\{[^}]*--slider-active: var\\(--md-sys-color-{$active}\\);[^}]*--slider-inactive: var\\(--md-sys-color-{$inactive}\\);/");
}
expect($css)->toMatch('/\[data-md-slider\]:has\(\[data-md-slider-input\]:disabled\) \{[^}]*--slider-active: var\(--slider-disabled-active\);[^}]*--slider-inactive: var\(--slider-disabled-inactive\);/');
})->with([
'primary' => ['primary', 'primary', 'secondary-container'],
'tertiary' => ['tertiary', 'tertiary', 'tertiary-container'],
'error' => ['error', 'error', 'error-container'],
'unknown' => ['pink', 'primary', 'secondary-container'],
]);