mirror of
https://github.com/snipe/snipe-it.git
synced 2026-08-18 11:15:42 +00:00
Added blade components for checkboxes, radios, and inline checkboxes
This commit is contained in:
@ -84,23 +84,17 @@
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-md-9 col-md-offset-3">
|
||||
<label class="form-control">
|
||||
<input type="checkbox" id="enable_sounds" name="enable_sounds" value="1" {{ old('enable_sounds', $user->enable_sounds) ? 'checked' : '' }}>
|
||||
{{ trans('account/general.enable_sounds') }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<x-form.checkbox-row
|
||||
name="enable_sounds"
|
||||
:label="trans('account/general.enable_sounds')"
|
||||
:item="$user"
|
||||
/>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-md-9 col-md-offset-3">
|
||||
<label class="form-control">
|
||||
<input type="checkbox" name="enable_confetti" id="enable_confetti" value="1" {{ old('enable_confetti', $user->enable_confetti) ? 'checked' : '' }}>
|
||||
{{ trans('account/general.enable_confetti') }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<x-form.checkbox-row
|
||||
name="enable_confetti"
|
||||
:label="trans('account/general.enable_confetti')"
|
||||
:item="$user"
|
||||
/>
|
||||
|
||||
|
||||
|
||||
|
||||
41
resources/views/blade/form/checkbox-inline.blade.php
Normal file
41
resources/views/blade/form/checkbox-inline.blade.php
Normal file
@ -0,0 +1,41 @@
|
||||
@props([
|
||||
'name' => null,
|
||||
'item' => null,
|
||||
'label' => null,
|
||||
'value' => '1',
|
||||
'required' => null,
|
||||
'disabled' => false,
|
||||
])
|
||||
|
||||
@php
|
||||
// Old-input aware check-state. On a fresh render, session()->hasOldInput()
|
||||
// is false, so we fall back to the model. On a validation-failure redisplay,
|
||||
// hasOldInput() is true and we trust the (possibly missing) old value — an
|
||||
// unchecked box comes back correctly unchecked instead of falling through
|
||||
// to the stale $item->{$name}.
|
||||
$is_redisplay = session()->hasOldInput();
|
||||
$checked = $is_redisplay
|
||||
? (bool) old($name)
|
||||
: (bool) ($item?->{$name} ?? false);
|
||||
|
||||
// Helper::checkIfRequired dereferences $item statically via $item::rules(),
|
||||
// so it needs a real class/object. Fall back to false when no model was
|
||||
// supplied (transient forms have no persistent model).
|
||||
$really_required = $required ?? ($item ? Helper::checkIfRequired($item, $name) : false);
|
||||
@endphp
|
||||
|
||||
{{-- Inline variant: no form-group wrapper. The caller drops this into an
|
||||
existing form row (e.g. next to a text input in a bulk-edit view) and
|
||||
controls the containing column themselves. --}}
|
||||
<label class="form-control">
|
||||
<x-input.checkbox
|
||||
:name="$name"
|
||||
:id="$name"
|
||||
:value="$value"
|
||||
:checked="$checked"
|
||||
:required="$really_required"
|
||||
:disabled="$disabled"
|
||||
:aria-label="$name"
|
||||
/>
|
||||
{{ $label }}
|
||||
</label>
|
||||
144
resources/views/blade/form/checkbox-row.blade.php
Normal file
144
resources/views/blade/form/checkbox-row.blade.php
Normal file
@ -0,0 +1,144 @@
|
||||
@props([
|
||||
'name' => null,
|
||||
'item' => null,
|
||||
'label' => null,
|
||||
'options' => null,
|
||||
'selected' => null,
|
||||
'value' => '1',
|
||||
'required' => null,
|
||||
'disabled' => false,
|
||||
'help_text' => null,
|
||||
'info_tooltip_text' => null,
|
||||
// Default input column: only skip the offset when a left-hand label
|
||||
// column is being rendered (i.e. multi mode with a label). Single mode
|
||||
// never has a left label; multi mode without a label lays out the same
|
||||
// way. Keeping the grid classes centralized here means a future Bootstrap /
|
||||
// AdminLTE upgrade only has to touch this file, not every callsite.
|
||||
// Note: Blade evaluates @props defaults twice (once via extractPropNames
|
||||
// before caller attrs are bound, once when applying defaults after). The
|
||||
// `?? null` guards against "undefined variable" on the first pass; isset()
|
||||
// is inherently safe against undefined vars, is_array() is not.
|
||||
'input_div_class' => (is_array($options ?? null) && isset($label)) ? 'col-md-8' : 'col-md-8 col-md-offset-3',
|
||||
])
|
||||
|
||||
@php
|
||||
// Multi-checkbox mode kicks in when the caller supplies an $options map;
|
||||
// otherwise this renders as a single boolean checkbox.
|
||||
$is_multi = is_array($options);
|
||||
|
||||
// Old-input aware check-state. On a fresh render, session()->hasOldInput()
|
||||
// is false, so we fall back to the model (or supplied :selected). On a
|
||||
// validation-failure redisplay, hasOldInput() is true and we trust the
|
||||
// (possibly missing) old value — an unchecked box comes back correctly
|
||||
// unchecked instead of falling through to the stale $item->{$name}.
|
||||
$is_redisplay = session()->hasOldInput();
|
||||
|
||||
if (! $is_multi) {
|
||||
$single_checked = $is_redisplay
|
||||
? (bool) old($name)
|
||||
: (bool) ($item?->{$name} ?? false);
|
||||
|
||||
// Helper::checkIfRequired dereferences $item statically via $item::rules(),
|
||||
// so it needs a real class/object. Fall back to false when no model was
|
||||
// supplied (transient forms have no persistent model).
|
||||
$really_required = $required ?? ($item ? Helper::checkIfRequired($item, $name) : false);
|
||||
} else {
|
||||
// For multi mode, callers can pass :selected as an array of currently-
|
||||
// selected values, a comma-joined string (common when the model stores
|
||||
// it that way, e.g. Setting's modellist_displays), or a callable
|
||||
// (value): bool for per-value predicates. When :selected is omitted
|
||||
// the same fallback is applied to $item->{$name}.
|
||||
if ($selected === null) {
|
||||
$selected = $item?->{$name};
|
||||
}
|
||||
|
||||
if (is_string($selected)) {
|
||||
$selected = $selected === '' ? [] : array_map('trim', explode(',', $selected));
|
||||
}
|
||||
|
||||
$old_values = is_array(old($name)) ? old($name) : [];
|
||||
|
||||
$is_checked = function ($value) use ($is_redisplay, $old_values, $selected) {
|
||||
if ($is_redisplay) {
|
||||
return in_array($value, $old_values);
|
||||
}
|
||||
if (is_callable($selected)) {
|
||||
return (bool) $selected($value);
|
||||
}
|
||||
return in_array($value, is_array($selected) ? $selected : []);
|
||||
};
|
||||
}
|
||||
|
||||
$errors_class = $errors->has($name) ? ' has-error' : '';
|
||||
@endphp
|
||||
|
||||
<div {{ $attributes->merge(['class' => 'form-group'.$errors_class]) }}>
|
||||
|
||||
@if (! $is_multi)
|
||||
|
||||
{{-- Single checkbox: no left-hand label column; label wraps the input. --}}
|
||||
<div class="{{ $input_div_class }}">
|
||||
<label class="form-control">
|
||||
<x-input.checkbox
|
||||
:name="$name"
|
||||
:id="$name"
|
||||
:value="$value"
|
||||
:checked="$single_checked"
|
||||
:required="$really_required"
|
||||
:disabled="$disabled"
|
||||
:aria-label="$name"
|
||||
/>
|
||||
{{ $label }}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@else
|
||||
|
||||
{{-- Multi: standard left-hand label + a stack of wrapped checkboxes on the right. --}}
|
||||
@if (isset($label))
|
||||
<x-form.label :for="$name" class="col-md-3">{{ $label }}</x-form.label>
|
||||
@endif
|
||||
|
||||
<div class="{{ $input_div_class }}">
|
||||
@foreach ($options as $option_value => $option_label)
|
||||
<label class="form-control">
|
||||
<x-input.checkbox
|
||||
:name="$name.'[]'"
|
||||
:value="$option_value"
|
||||
:checked="$is_checked($option_value)"
|
||||
:disabled="$disabled"
|
||||
:aria-label="$name"
|
||||
/>
|
||||
{{ $option_label }}
|
||||
</label>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
@endif
|
||||
|
||||
@if ($info_tooltip_text)
|
||||
<div class="col-md-1 text-left" style="padding-left:0; margin-top: 5px;">
|
||||
<x-form.tooltip>
|
||||
{{ $info_tooltip_text }}
|
||||
</x-form.tooltip>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@error($name)
|
||||
<div class="col-md-8 col-md-offset-3">
|
||||
<span class="alert-msg" aria-hidden="true">
|
||||
<x-icon type="x" />
|
||||
{{ $message }}
|
||||
</span>
|
||||
</div>
|
||||
@enderror
|
||||
|
||||
@if ($help_text)
|
||||
<div class="col-md-8 col-md-offset-3">
|
||||
<p class="help-block">
|
||||
{!! $help_text !!}
|
||||
</p>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
87
resources/views/blade/form/radio-row.blade.php
Normal file
87
resources/views/blade/form/radio-row.blade.php
Normal file
@ -0,0 +1,87 @@
|
||||
@props([
|
||||
'name' => null,
|
||||
'item' => null,
|
||||
'label' => null,
|
||||
'options' => [],
|
||||
'selected' => null,
|
||||
'required' => null,
|
||||
'disabled' => false,
|
||||
'help_text' => null,
|
||||
'info_tooltip_text' => null,
|
||||
// Default input column depends on whether the row has a left-hand label.
|
||||
// With a label, the row already spends col-md-3 on the left; without one
|
||||
// the options need to be offset. Concentrating the grid class here means
|
||||
// a future Bootstrap / AdminLTE upgrade only has to touch this file, not
|
||||
// every place it's invoked.
|
||||
'input_div_class' => isset($label) ? 'col-md-8' : 'col-md-8 col-md-offset-3',
|
||||
])
|
||||
|
||||
@php
|
||||
// Redisplay-safe current value. On validation-failure redisplay
|
||||
// session()->hasOldInput() is true and we trust old($name); on fresh
|
||||
// render we take the caller's :selected value, or fall back to the
|
||||
// model attribute. This is the same guard as checkbox-row: an old-input
|
||||
// value of null on redisplay means "nothing selected", not "fall back
|
||||
// to the stale model default".
|
||||
$is_redisplay = session()->hasOldInput();
|
||||
if ($is_redisplay) {
|
||||
$current_value = old($name);
|
||||
} else {
|
||||
$current_value = $selected ?? $item?->{$name};
|
||||
}
|
||||
|
||||
// Helper::checkIfRequired dereferences $item statically via $item::rules(),
|
||||
// so it needs a real class/object. Fall back to false when no model was
|
||||
// supplied (transient forms like bulk checkin have no persistent model).
|
||||
$really_required = $required ?? ($item ? Helper::checkIfRequired($item, $name) : false);
|
||||
$errors_class = $errors->has($name) ? ' has-error' : '';
|
||||
@endphp
|
||||
|
||||
<div {{ $attributes->merge(['class' => 'form-group'.$errors_class]) }}>
|
||||
|
||||
@if (isset($label))
|
||||
<x-form.label :for="$name" class="col-md-3">{{ $label }}</x-form.label>
|
||||
@endif
|
||||
|
||||
<div class="{{ $input_div_class }}">
|
||||
@foreach ($options as $option_value => $option_label)
|
||||
<label class="form-control">
|
||||
<x-input.radio
|
||||
:name="$name"
|
||||
:value="$option_value"
|
||||
:checked="$current_value !== null && (string) $current_value === (string) $option_value"
|
||||
:required="$really_required && $loop->first"
|
||||
:disabled="$disabled"
|
||||
:aria-label="$name"
|
||||
/>
|
||||
{{ $option_label }}
|
||||
</label>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
@if ($info_tooltip_text)
|
||||
<div class="col-md-1 text-left" style="padding-left:0; margin-top: 5px;">
|
||||
<x-form.tooltip>
|
||||
{{ $info_tooltip_text }}
|
||||
</x-form.tooltip>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@error($name)
|
||||
<div class="col-md-8 col-md-offset-3">
|
||||
<span class="alert-msg" aria-hidden="true">
|
||||
<x-icon type="x" />
|
||||
{{ $message }}
|
||||
</span>
|
||||
</div>
|
||||
@enderror
|
||||
|
||||
@if ($help_text)
|
||||
<div class="col-md-8 col-md-offset-3">
|
||||
<p class="help-block">
|
||||
{!! $help_text !!}
|
||||
</p>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
19
resources/views/blade/input/checkbox.blade.php
Normal file
19
resources/views/blade/input/checkbox.blade.php
Normal file
@ -0,0 +1,19 @@
|
||||
@props([
|
||||
'name' => null,
|
||||
'value' => '1',
|
||||
'checked' => false,
|
||||
'required' => false,
|
||||
'disabled' => false,
|
||||
'id' => null,
|
||||
])
|
||||
<!-- input-checkbox blade component -->
|
||||
<input
|
||||
type="checkbox"
|
||||
name="{{ $name }}"
|
||||
value="{{ $value }}"
|
||||
@if ($id) id="{{ $id }}" @endif
|
||||
{{ $attributes }}
|
||||
@checked($checked)
|
||||
@required($required)
|
||||
@disabled($disabled)
|
||||
/>
|
||||
19
resources/views/blade/input/radio.blade.php
Normal file
19
resources/views/blade/input/radio.blade.php
Normal file
@ -0,0 +1,19 @@
|
||||
@props([
|
||||
'name' => null,
|
||||
'value' => null,
|
||||
'checked' => false,
|
||||
'required' => false,
|
||||
'disabled' => false,
|
||||
'id' => null,
|
||||
])
|
||||
<!-- input-radio blade component -->
|
||||
<input
|
||||
type="radio"
|
||||
name="{{ $name }}"
|
||||
value="{{ $value }}"
|
||||
@if ($id) id="{{ $id }}" @endif
|
||||
{{ $attributes }}
|
||||
@checked($checked)
|
||||
@required($required)
|
||||
@disabled($disabled)
|
||||
/>
|
||||
@ -81,18 +81,14 @@
|
||||
/>
|
||||
|
||||
<!-- Update actual location -->
|
||||
<div class="form-group">
|
||||
<div class="col-md-9 col-md-offset-3">
|
||||
<label class="form-control">
|
||||
<input name="update_default_location" type="radio" value="1" @checked(old('update_default_location', '1') == '1') aria-label="update_default_location" />
|
||||
{{ trans('admin/hardware/form.asset_location') }}
|
||||
</label>
|
||||
<label class="form-control">
|
||||
<input name="update_default_location" type="radio" value="0" @checked(old('update_default_location') === '0') aria-label="update_default_location" />
|
||||
{{ trans('admin/hardware/form.asset_location_update_default_current') }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<x-form.radio-row
|
||||
name="update_default_location"
|
||||
selected="1"
|
||||
:options="[
|
||||
'1' => trans('admin/hardware/form.asset_location'),
|
||||
'0' => trans('admin/hardware/form.asset_location_update_default_current'),
|
||||
]"
|
||||
/>
|
||||
|
||||
<!-- Checkin Date -->
|
||||
<div class="form-group {{ $errors->has('checkin_at') ? 'error' : '' }}">
|
||||
|
||||
@ -46,10 +46,10 @@
|
||||
<i class="fas fa-times" aria-hidden="true"></i> :message</span>') !!}
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<label class="form-control">
|
||||
<input type="checkbox" name="null_name" value="1">
|
||||
{{ trans_choice('general.set_to_null', count($assets), ['selection_count' => count($assets)]) }}
|
||||
</label>
|
||||
<x-form.checkbox-inline
|
||||
name="null_name"
|
||||
:label="trans_choice('general.set_to_null', count($assets), ['selection_count' => count($assets)])"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -67,10 +67,10 @@
|
||||
{!! $errors->first('purchase_date', '<span class="alert-msg" aria-hidden="true"><i class="fas fa-times" aria-hidden="true"></i> :message</span>') !!}
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<label class="form-control">
|
||||
<input type="checkbox" name="null_purchase_date" value="1">
|
||||
{{ trans_choice('general.set_to_null', count($assets),['selection_count' => count($assets)]) }}
|
||||
</label>
|
||||
<x-form.checkbox-inline
|
||||
name="null_purchase_date"
|
||||
:label="trans_choice('general.set_to_null', count($assets), ['selection_count' => count($assets)])"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -87,10 +87,10 @@
|
||||
{!! $errors->first('expected_checkin', '<span class="alert-msg" aria-hidden="true"><i class="fas fa-times" aria-hidden="true"></i> :message</span>') !!}
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<label class="form-control">
|
||||
<input type="checkbox" name="null_expected_checkin_date" value="1">
|
||||
{{ trans_choice('general.set_to_null', count($assets),['selection_count' => count($assets)]) }}
|
||||
</label>
|
||||
<x-form.checkbox-inline
|
||||
name="null_expected_checkin_date"
|
||||
:label="trans_choice('general.set_to_null', count($assets), ['selection_count' => count($assets)])"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -107,10 +107,10 @@
|
||||
{!! $errors->first('asset_eol_date', '<span class="alert-msg" aria-hidden="true"><i class="fas fa-times" aria-hidden="true"></i> :message</span>') !!}
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<label class="form-control">
|
||||
<input type="checkbox" name="null_asset_eol_date" value="1">
|
||||
{{ trans_choice('general.set_to_null', count($assets),['selection_count' => count($assets)]) }}
|
||||
</label>
|
||||
<x-form.checkbox-inline
|
||||
name="null_asset_eol_date"
|
||||
:label="trans_choice('general.set_to_null', count($assets), ['selection_count' => count($assets)])"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -225,10 +225,10 @@
|
||||
{!! $errors->first('next_audit_date', '<span class="alert-msg" aria-hidden="true"><i class="fas fa-times" aria-hidden="true"></i> :message</span>') !!}
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<label class="form-control">
|
||||
<input type="checkbox" name="null_next_audit_date" value="1">
|
||||
{{ trans_choice('general.set_to_null', count($assets),['selection_count' => count($assets)]) }}
|
||||
</label>
|
||||
<x-form.checkbox-inline
|
||||
name="null_next_audit_date"
|
||||
:label="trans_choice('general.set_to_null', count($assets), ['selection_count' => count($assets)])"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-md-8 col-md-offset-3">
|
||||
<p class="help-block">{!! trans('general.next_audit_date_help') !!}</p>
|
||||
|
||||
@ -145,18 +145,14 @@
|
||||
/>
|
||||
|
||||
<!-- Update actual location -->
|
||||
<div class="form-group">
|
||||
<div class="col-md-9 col-md-offset-3">
|
||||
<label class="form-control">
|
||||
<input name="update_default_location" type="radio" value="1" checked="checked" aria-label="update_default_location" />
|
||||
{{ trans('admin/hardware/form.asset_location') }}
|
||||
</label>
|
||||
<label class="form-control">
|
||||
<input name="update_default_location" type="radio" value="0" aria-label="update_default_location" />
|
||||
{{ trans('admin/hardware/form.asset_location_update_default_current') }}
|
||||
</label>
|
||||
</div>
|
||||
</div> <!--/form-group-->
|
||||
<x-form.radio-row
|
||||
name="update_default_location"
|
||||
selected="1"
|
||||
:options="[
|
||||
'1' => trans('admin/hardware/form.asset_location'),
|
||||
'0' => trans('admin/hardware/form.asset_location_update_default_current'),
|
||||
]"
|
||||
/> <!--/form-group-->
|
||||
|
||||
<!-- Checkout/Checkin Date -->
|
||||
<div class="form-group{{ $errors->has('checkin_at') ? ' has-error' : '' }}">
|
||||
|
||||
@ -102,25 +102,15 @@
|
||||
</div>
|
||||
|
||||
<!-- requestable -->
|
||||
<div class="form-group{{ $errors->has('requestable') ? ' has-error' : '' }}">
|
||||
<div class="col-md-7 col-md-offset-3">
|
||||
|
||||
<label for="requestable_nochange" class="form-control">
|
||||
<input type="radio" name="requestable" id="requestable_nochange" value="" aria-label="requestable_nochange" checked>
|
||||
{{ trans('admin/hardware/general.requestable_status_warning')}}
|
||||
</label>
|
||||
<label for="requestable" class="form-control">
|
||||
<input type="radio" name="requestable" id="requestable" value="1" aria-label="requestable">
|
||||
{{ trans('admin/hardware/general.requestable')}}
|
||||
</label>
|
||||
<label for="not_requestable" class="form-control">
|
||||
<input type="radio" name="requestable" id="not_requestable" value="0" aria-label="not_requestable">
|
||||
{{ trans('admin/hardware/general.not_requestable')}}
|
||||
</label>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<x-form.radio-row
|
||||
name="requestable"
|
||||
selected=""
|
||||
:options="[
|
||||
'' => trans('admin/hardware/general.requestable_status_warning'),
|
||||
'1' => trans('admin/hardware/general.requestable'),
|
||||
'0' => trans('admin/hardware/general.not_requestable'),
|
||||
]"
|
||||
/>
|
||||
|
||||
@foreach ($models as $model)
|
||||
<input type="hidden" name="ids[{{ $model->id }}]" value="{{ $model->id }}">
|
||||
|
||||
@ -420,38 +420,15 @@
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="col-md-9 col-md-offset-3">
|
||||
<label class="form-control">
|
||||
<input
|
||||
name="deleted_components"
|
||||
id="deleted_components_exclude_deleted"
|
||||
type="radio"
|
||||
value="exclude_deleted"
|
||||
@checked($template->radioValue('deleted_components', 'exclude_deleted', true))
|
||||
aria-label="deleted_components"
|
||||
>{{ trans('admin/components/general.exclude_deleted') }}
|
||||
</label>
|
||||
<label class="form-control">
|
||||
<input
|
||||
name="deleted_components"
|
||||
id="deleted_components_include_deleted"
|
||||
type="radio"
|
||||
value="include_deleted"
|
||||
@checked($template->radioValue('deleted_components', 'include_deleted'))
|
||||
aria-label="deleted_components"
|
||||
>{{ trans('admin/components/general.include_deleted') }}
|
||||
</label>
|
||||
<label class="form-control">
|
||||
<input
|
||||
name="deleted_components"
|
||||
type="radio"
|
||||
id="deleted_components_only_deleted"
|
||||
value="only_deleted"
|
||||
@checked($template->radioValue('deleted_components', 'only_deleted'))
|
||||
aria-label="deleted_components"
|
||||
>{{ trans('admin/components/general.only_deleted') }}
|
||||
</label>
|
||||
</div>
|
||||
<x-form.radio-row
|
||||
name="deleted_components"
|
||||
:selected="$template->options['deleted_components'] ?? 'exclude_deleted'"
|
||||
:options="[
|
||||
'exclude_deleted' => trans('admin/components/general.exclude_deleted'),
|
||||
'include_deleted' => trans('admin/components/general.include_deleted'),
|
||||
'only_deleted' => trans('admin/components/general.only_deleted'),
|
||||
]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div> <!-- /.box-body-->
|
||||
|
||||
@ -47,24 +47,18 @@
|
||||
</x-form.legend>
|
||||
|
||||
<!-- Menu Alerts Enabled -->
|
||||
<div class="form-group{{ $errors->has('show_alerts_in_menu') ? ' error' : '' }}">
|
||||
<div class="col-md-9 col-md-offset-3">
|
||||
<label class="form-control">
|
||||
<input type="checkbox" name="show_alerts_in_menu" value="1" @checked(old('show_alerts_in_menu', $setting->show_alerts_in_menu))>
|
||||
{{ trans('admin/settings/general.show_alerts_in_menu') }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<x-form.checkbox-row
|
||||
name="show_alerts_in_menu"
|
||||
:label="trans('admin/settings/general.show_alerts_in_menu')"
|
||||
:item="$setting"
|
||||
/>
|
||||
|
||||
<!-- Alerts Enabled -->
|
||||
<div class="form-group {{ $errors->has('alerts_enabled') ? 'error' : '' }}">
|
||||
<div class="col-md-9 col-md-offset-3">
|
||||
<label class="form-control">
|
||||
<input type="checkbox" name="alerts_enabled" value="1" @checked(old('alerts_enabled', $setting->alerts_enabled))>
|
||||
{{ trans('admin/settings/general.alerts_enabled') }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<x-form.checkbox-row
|
||||
name="alerts_enabled"
|
||||
:label="trans('admin/settings/general.alerts_enabled')"
|
||||
:item="$setting"
|
||||
/>
|
||||
|
||||
</fieldset>
|
||||
|
||||
@ -106,28 +100,14 @@
|
||||
{!! $errors->first('admin_cc_email', '<span class="alert-msg" aria-hidden="true">:message</span><br>') !!}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-md-9 col-md-offset-3">
|
||||
<label class="form-control">
|
||||
<input
|
||||
type="radio"
|
||||
name="admin_cc_always"
|
||||
value="1"
|
||||
@checked($setting->admin_cc_always == 1)
|
||||
>
|
||||
{{ trans('admin/settings/general.admin_cc_always') }}
|
||||
</label>
|
||||
<label class="form-control">
|
||||
<input
|
||||
type="radio"
|
||||
name="admin_cc_always"
|
||||
value="0"
|
||||
@checked($setting->admin_cc_always == 0)
|
||||
>
|
||||
{{ trans('admin/settings/general.admin_cc_when_acceptance_required') }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<x-form.radio-row
|
||||
name="admin_cc_always"
|
||||
:item="$setting"
|
||||
:options="[
|
||||
'1' => trans('admin/settings/general.admin_cc_always'),
|
||||
'0' => trans('admin/settings/general.admin_cc_when_acceptance_required'),
|
||||
]"
|
||||
/>
|
||||
</fieldset>
|
||||
|
||||
<fieldset name="remote-login">
|
||||
|
||||
@ -39,18 +39,12 @@
|
||||
{{ trans('admin/settings/general.legends.scoping') }}
|
||||
</x-form.legend>
|
||||
<!-- Full Multiple Companies Support -->
|
||||
<div class="form-group {{ $errors->has('full_multiple_companies_support') ? 'error' : '' }}">
|
||||
<div class="col-md-8 col-md-offset-3">
|
||||
<label class="form-control">
|
||||
<input type="checkbox" name="full_multiple_companies_support" value="1" @checked(old('full_multiple_companies_support', $setting->full_multiple_companies_support)) aria-label="full_multiple_companies_support" />
|
||||
{{ trans('admin/settings/general.full_multiple_companies_support_text') }}
|
||||
</label>
|
||||
{!! $errors->first('full_multiple_companies_support', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
<p class="help-block">
|
||||
{{ trans('admin/settings/general.full_multiple_companies_support_help_text') }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<x-form.checkbox-row
|
||||
name="full_multiple_companies_support"
|
||||
:label="trans('admin/settings/general.full_multiple_companies_support_text')"
|
||||
:item="$setting"
|
||||
:help_text="trans('admin/settings/general.full_multiple_companies_support_help_text')"
|
||||
/>
|
||||
<!-- /.form-group -->
|
||||
|
||||
<!-- Scope Locations with Full Multiple Companies Support -->
|
||||
@ -62,18 +56,13 @@
|
||||
<!-- /.form-group -->
|
||||
|
||||
<!-- Null Company Is Floater -->
|
||||
<div class="form-group {{ $errors->has('null_company_is_floater') ? 'error' : '' }}">
|
||||
<div class="col-md-8 col-md-offset-3">
|
||||
<label class="form-control">
|
||||
<input type="checkbox" name="null_company_is_floater" value="1" @checked(old('null_company_is_floater', $setting->null_company_is_floater)) aria-label="null_company_is_floater" @disabled(! $setting->full_multiple_companies_support) />
|
||||
{{ trans('admin/settings/general.null_company_is_floater_text') }}
|
||||
</label>
|
||||
{!! $errors->first('null_company_is_floater', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
<p class="help-block">
|
||||
{{ trans('admin/settings/general.null_company_is_floater_help_text') }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<x-form.checkbox-row
|
||||
name="null_company_is_floater"
|
||||
:label="trans('admin/settings/general.null_company_is_floater_text')"
|
||||
:item="$setting"
|
||||
:disabled="! $setting->full_multiple_companies_support"
|
||||
:help_text="trans('admin/settings/general.null_company_is_floater_help_text')"
|
||||
/>
|
||||
<!-- /.form-group -->
|
||||
|
||||
</fieldset>
|
||||
@ -156,16 +145,12 @@
|
||||
</x-form.legend>
|
||||
|
||||
<!-- Require signature for acceptance -->
|
||||
<div class="form-group {{ $errors->has('require_accept_signature') ? 'error' : '' }}">
|
||||
<div class="col-md-8 col-md-offset-3">
|
||||
<label class="form-control">
|
||||
<input type="checkbox" name="require_accept_signature" value="1" @checked(old('require_accept_signature', $setting->require_accept_signature)) />
|
||||
{{ trans('admin/settings/general.require_accept_signature') }}
|
||||
</label>
|
||||
{!! $errors->first('require_accept_signature', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
<p class="help-block">{{ trans('admin/settings/general.require_accept_signature_help_text') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<x-form.checkbox-row
|
||||
name="require_accept_signature"
|
||||
:label="trans('admin/settings/general.require_accept_signature')"
|
||||
:item="$setting"
|
||||
:help_text="trans('admin/settings/general.require_accept_signature_help_text')"
|
||||
/>
|
||||
<!-- /.form-group -->
|
||||
|
||||
<!-- Default EULA -->
|
||||
@ -204,53 +189,34 @@
|
||||
</div>
|
||||
|
||||
<!-- Model List prefs -->
|
||||
<div class="form-group {{ $errors->has('show_in_model_list') ? 'error' : '' }}">
|
||||
<div class="col-md-3">
|
||||
<strong>{{ trans('admin/settings/general.show_in_model_list') }}</strong>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<label class="form-control">
|
||||
<input type="checkbox" name="show_in_model_list[]" value="image" @checked(old('show_in_model_list', $snipeSettings->modellistCheckedValue('image'))) aria-label="show_in_model_list"/>
|
||||
{{ trans('general.image') }}
|
||||
</label>
|
||||
<label class="form-control">
|
||||
<input type="checkbox" name="show_in_model_list[]" value="category" @checked(old('show_in_model_list', $snipeSettings->modellistCheckedValue('category'))) aria-label="show_in_model_list"/>
|
||||
{{ trans('general.category') }}
|
||||
</label>
|
||||
<label class="form-control">
|
||||
<input type="checkbox" name="show_in_model_list[]" value="manufacturer" @checked(old('show_in_model_list', $snipeSettings->modellistCheckedValue('manufacturer'))) aria-label="show_in_model_list"/>
|
||||
{{ trans('general.manufacturer') }} </label>
|
||||
<label class="form-control">
|
||||
<input type="checkbox" name="show_in_model_list[]" value="model_number" @checked(old('show_in_model_list', $snipeSettings->modellistCheckedValue('model_number'))) aria-label="show_in_model_list"/>
|
||||
{{ trans('general.model_no') }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<x-form.checkbox-row
|
||||
name="show_in_model_list"
|
||||
:label="trans('admin/settings/general.show_in_model_list')"
|
||||
:options="[
|
||||
'image' => trans('general.image'),
|
||||
'category' => trans('general.category'),
|
||||
'manufacturer' => trans('general.manufacturer'),
|
||||
'model_number' => trans('general.model_no'),
|
||||
]"
|
||||
:selected="$snipeSettings->modellist_displays"
|
||||
/>
|
||||
|
||||
|
||||
<!-- Shortcuts enable -->
|
||||
<div class="form-group {{ $errors->has('shortcuts_enabled') ? 'error' : '' }}">
|
||||
<div class="col-md-8 col-md-offset-3">
|
||||
<label class="form-control">
|
||||
<input type="checkbox" name="shortcuts_enabled" value="1" {{ old('shortcuts_enabled', $setting->shortcuts_enabled) ? 'checked' : '' }}>
|
||||
{{ trans('admin/settings/general.shortcuts_enabled') }}
|
||||
</label>
|
||||
{!! $errors->first('shortcuts_enabled', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
<p class="help-block">{!!trans('admin/settings/general.shortcuts_help_text') !!}</p>
|
||||
</div>
|
||||
</div>
|
||||
<x-form.checkbox-row
|
||||
name="shortcuts_enabled"
|
||||
:label="trans('admin/settings/general.shortcuts_enabled')"
|
||||
:item="$setting"
|
||||
:help_text="trans('admin/settings/general.shortcuts_help_text')"
|
||||
/>
|
||||
|
||||
|
||||
<!-- Archived in List -->
|
||||
<div class="form-group {{ $errors->has('show_archived_in_list') ? 'error' : '' }}">
|
||||
<div class="col-md-8 col-md-offset-3">
|
||||
<label class="form-control">
|
||||
<input type="checkbox" name="show_archived_in_list" value="1" @checked(old('show_archived_in_list', $setting->show_archived_in_list)) aria-label="show_archived_in_list" />
|
||||
{{ trans('admin/settings/general.show_archived_in_list_text') }}
|
||||
</label>
|
||||
{!! $errors->first('show_archived_in_list', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
</div>
|
||||
</div>
|
||||
<x-form.checkbox-row
|
||||
name="show_archived_in_list"
|
||||
:label="trans('admin/settings/general.show_archived_in_list_text')"
|
||||
:item="$setting"
|
||||
/>
|
||||
|
||||
<!-- Show assets assigned to user's assets -->
|
||||
<div class="form-group {{ $errors->has('show_assigned_assets') ? 'error' : '' }}">
|
||||
|
||||
130
tests/Feature/Blade/CheckboxInlineTest.php
Normal file
130
tests/Feature/Blade/CheckboxInlineTest.php
Normal file
@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Blade;
|
||||
|
||||
use App\Http\Middleware\CheckForDebug;
|
||||
use App\Http\Middleware\CheckForSetup;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Tests\TestCase;
|
||||
|
||||
class CheckboxInlineTest extends TestCase
|
||||
{
|
||||
private function render(array $data, ?array $oldInput = null): string
|
||||
{
|
||||
Route::get('/__test/checkbox-inline', function () use ($data) {
|
||||
return view('blade.form.checkbox-inline', $data);
|
||||
});
|
||||
|
||||
$call = $this->withoutMiddleware([
|
||||
CheckForSetup::class,
|
||||
CheckForDebug::class,
|
||||
]);
|
||||
if ($oldInput !== null) {
|
||||
$call = $call->withSession(['_old_input' => $oldInput]);
|
||||
}
|
||||
|
||||
return $call->get('/__test/checkbox-inline')->assertOk()->getContent();
|
||||
}
|
||||
|
||||
private function item(): object
|
||||
{
|
||||
return new class
|
||||
{
|
||||
public bool $enabled = true;
|
||||
|
||||
public bool $disabled_flag = false;
|
||||
|
||||
public static function rules()
|
||||
{
|
||||
return ['enabled' => 'required|boolean'];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public function test_does_not_emit_form_group_wrapper()
|
||||
{
|
||||
// Inline variant must not carry a form-group class; the caller
|
||||
// controls its own row layout in bulk-edit views.
|
||||
$html = $this->render([
|
||||
'name' => 'null_name',
|
||||
'label' => 'Set to null',
|
||||
]);
|
||||
|
||||
$this->assertStringNotContainsString('form-group', $html);
|
||||
$this->assertStringContainsString('<label class="form-control">', $html);
|
||||
}
|
||||
|
||||
public function test_no_item_does_not_crash_when_deriving_required()
|
||||
{
|
||||
// The classic bulk-edit case: transient sentinel with no model.
|
||||
$html = $this->render([
|
||||
'name' => 'null_name',
|
||||
'label' => 'Set to null',
|
||||
]);
|
||||
|
||||
$this->assertMatchesRegularExpression('/type="checkbox"[^>]*name="null_name"/', $html);
|
||||
$this->assertDoesNotMatchRegularExpression('/name="null_name"[^>]*required/', $html);
|
||||
}
|
||||
|
||||
public function test_fresh_render_reads_model_value()
|
||||
{
|
||||
$html = $this->render([
|
||||
'name' => 'enabled',
|
||||
'label' => 'Enabled',
|
||||
'item' => $this->item(),
|
||||
]);
|
||||
|
||||
$this->assertMatchesRegularExpression('/name="enabled"[^>]*checked/', $html);
|
||||
}
|
||||
|
||||
public function test_redisplay_unchecked_does_not_fall_back_to_model()
|
||||
{
|
||||
// Model says true; user unchecked before submit; validation failed
|
||||
// elsewhere. On redisplay the box must stay unchecked.
|
||||
$html = $this->render(
|
||||
data: [
|
||||
'name' => 'enabled',
|
||||
'label' => 'Enabled',
|
||||
'item' => $this->item(),
|
||||
],
|
||||
oldInput: ['some_other_field' => 'x'],
|
||||
);
|
||||
|
||||
$this->assertDoesNotMatchRegularExpression('/name="enabled"[^>]*checked/', $html);
|
||||
}
|
||||
|
||||
public function test_redisplay_checked_shows_checked()
|
||||
{
|
||||
$html = $this->render(
|
||||
data: [
|
||||
'name' => 'null_name',
|
||||
'label' => 'Set to null',
|
||||
],
|
||||
oldInput: ['null_name' => '1'],
|
||||
);
|
||||
|
||||
$this->assertMatchesRegularExpression('/name="null_name"[^>]*checked/', $html);
|
||||
}
|
||||
|
||||
public function test_derives_required_from_model_rules()
|
||||
{
|
||||
$html = $this->render([
|
||||
'name' => 'enabled',
|
||||
'label' => 'Enabled',
|
||||
'item' => $this->item(),
|
||||
]);
|
||||
|
||||
$this->assertMatchesRegularExpression('/name="enabled"[^>]*required/', $html);
|
||||
}
|
||||
|
||||
public function test_disabled_propagates_to_input()
|
||||
{
|
||||
$html = $this->render([
|
||||
'name' => 'null_name',
|
||||
'label' => 'Set to null',
|
||||
'disabled' => true,
|
||||
]);
|
||||
|
||||
$this->assertMatchesRegularExpression('/name="null_name"[^>]*disabled/', $html);
|
||||
}
|
||||
}
|
||||
197
tests/Feature/Blade/CheckboxRowTest.php
Normal file
197
tests/Feature/Blade/CheckboxRowTest.php
Normal file
@ -0,0 +1,197 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Blade;
|
||||
|
||||
use App\Http\Middleware\CheckForDebug;
|
||||
use App\Http\Middleware\CheckForSetup;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Tests\TestCase;
|
||||
|
||||
class CheckboxRowTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* Bind a temporary route that renders the checkbox-row component with
|
||||
* the caller-supplied data. Actual HTTP requests are what wire the
|
||||
* session onto the Request, so this is the shortest way to exercise
|
||||
* old() / session()->hasOldInput() correctly.
|
||||
*/
|
||||
private function render(array $data, ?array $oldInput = null): string
|
||||
{
|
||||
Route::get('/__test/checkbox-row', function () use ($data) {
|
||||
return view('blade.form.checkbox-row', $data);
|
||||
});
|
||||
|
||||
$call = $this->withoutMiddleware([
|
||||
CheckForSetup::class,
|
||||
CheckForDebug::class,
|
||||
]);
|
||||
if ($oldInput !== null) {
|
||||
$call = $call->withSession(['_old_input' => $oldInput]);
|
||||
}
|
||||
|
||||
return $call->get('/__test/checkbox-row')->assertOk()->getContent();
|
||||
}
|
||||
|
||||
private function item(): object
|
||||
{
|
||||
return new class
|
||||
{
|
||||
public bool $enabled = true;
|
||||
|
||||
public bool $disabled_flag = false;
|
||||
|
||||
public array $prefs = ['email', 'sms'];
|
||||
|
||||
public static function rules()
|
||||
{
|
||||
return ['enabled' => 'required|boolean'];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public function test_single_fresh_render_reads_model_value_true()
|
||||
{
|
||||
$html = $this->render([
|
||||
'name' => 'enabled',
|
||||
'label' => 'Enabled',
|
||||
'item' => $this->item(),
|
||||
]);
|
||||
|
||||
$this->assertMatchesRegularExpression('/name="enabled"[^>]*checked/', $html);
|
||||
}
|
||||
|
||||
public function test_single_fresh_render_reads_model_value_false()
|
||||
{
|
||||
$html = $this->render([
|
||||
'name' => 'disabled_flag',
|
||||
'label' => 'Disabled',
|
||||
'item' => $this->item(),
|
||||
]);
|
||||
|
||||
$this->assertDoesNotMatchRegularExpression('/name="disabled_flag"[^>]*checked/', $html);
|
||||
}
|
||||
|
||||
public function test_single_redisplay_unchecked_does_not_fall_back_to_model()
|
||||
{
|
||||
$html = $this->render(
|
||||
data: [
|
||||
'name' => 'enabled',
|
||||
'label' => 'Enabled',
|
||||
'item' => $this->item(),
|
||||
],
|
||||
oldInput: ['some_other_field' => 'x'],
|
||||
);
|
||||
|
||||
$this->assertDoesNotMatchRegularExpression('/name="enabled"[^>]*checked/', $html);
|
||||
}
|
||||
|
||||
public function test_single_redisplay_checked_shows_checked()
|
||||
{
|
||||
$html = $this->render(
|
||||
data: [
|
||||
'name' => 'disabled_flag',
|
||||
'label' => 'Disabled',
|
||||
'item' => $this->item(),
|
||||
],
|
||||
oldInput: ['disabled_flag' => '1'],
|
||||
);
|
||||
|
||||
$this->assertMatchesRegularExpression('/name="disabled_flag"[^>]*checked/', $html);
|
||||
}
|
||||
|
||||
public function test_multi_fresh_render_from_model_array_checks_matching_options()
|
||||
{
|
||||
$html = $this->render([
|
||||
'name' => 'prefs',
|
||||
'label' => 'Notifications',
|
||||
'options' => ['email' => 'Email', 'sms' => 'SMS', 'push' => 'Push'],
|
||||
'item' => $this->item(),
|
||||
]);
|
||||
|
||||
$this->assertMatchesRegularExpression('/value="email"[^>]*checked/', $html);
|
||||
$this->assertMatchesRegularExpression('/value="sms"[^>]*checked/', $html);
|
||||
$this->assertDoesNotMatchRegularExpression('/value="push"[^>]*checked/', $html);
|
||||
}
|
||||
|
||||
public function test_multi_fresh_render_with_callable_selected()
|
||||
{
|
||||
$html = $this->render([
|
||||
'name' => 'modellist_displays',
|
||||
'label' => 'Model Columns',
|
||||
'options' => ['image' => 'Image', 'category' => 'Category'],
|
||||
'selected' => fn ($v) => $v === 'image',
|
||||
]);
|
||||
|
||||
$this->assertMatchesRegularExpression('/value="image"[^>]*checked/', $html);
|
||||
$this->assertDoesNotMatchRegularExpression('/value="category"[^>]*checked/', $html);
|
||||
}
|
||||
|
||||
public function test_multi_redisplay_reflects_old_selection_not_model()
|
||||
{
|
||||
$html = $this->render(
|
||||
data: [
|
||||
'name' => 'prefs',
|
||||
'label' => 'Notifications',
|
||||
'options' => ['email' => 'Email', 'sms' => 'SMS', 'push' => 'Push'],
|
||||
'item' => $this->item(),
|
||||
],
|
||||
oldInput: ['prefs' => ['sms']],
|
||||
);
|
||||
|
||||
$this->assertDoesNotMatchRegularExpression('/value="email"[^>]*checked/', $html);
|
||||
$this->assertMatchesRegularExpression('/value="sms"[^>]*checked/', $html);
|
||||
$this->assertDoesNotMatchRegularExpression('/value="push"[^>]*checked/', $html);
|
||||
}
|
||||
|
||||
public function test_multi_redisplay_all_unchecked_does_not_fall_back_to_model()
|
||||
{
|
||||
$html = $this->render(
|
||||
data: [
|
||||
'name' => 'prefs',
|
||||
'label' => 'Notifications',
|
||||
'options' => ['email' => 'Email', 'sms' => 'SMS', 'push' => 'Push'],
|
||||
'item' => $this->item(),
|
||||
],
|
||||
oldInput: ['some_other_field' => 'x'],
|
||||
);
|
||||
|
||||
$this->assertDoesNotMatchRegularExpression('/value="email"[^>]*checked/', $html);
|
||||
$this->assertDoesNotMatchRegularExpression('/value="sms"[^>]*checked/', $html);
|
||||
$this->assertDoesNotMatchRegularExpression('/value="push"[^>]*checked/', $html);
|
||||
}
|
||||
|
||||
public function test_no_item_does_not_crash_when_deriving_required()
|
||||
{
|
||||
// Transient forms with no persistent model must not try to dereference
|
||||
// a null $item via checkIfRequired (which does $item::rules()).
|
||||
$html = $this->render([
|
||||
'name' => 'consent',
|
||||
'label' => 'I consent',
|
||||
]);
|
||||
|
||||
$this->assertDoesNotMatchRegularExpression('/name="consent"[^>]*required/', $html);
|
||||
}
|
||||
|
||||
public function test_single_derives_required_from_model_rules()
|
||||
{
|
||||
$html = $this->render([
|
||||
'name' => 'enabled',
|
||||
'label' => 'Enabled',
|
||||
'item' => $this->item(),
|
||||
]);
|
||||
|
||||
$this->assertMatchesRegularExpression('/name="enabled"[^>]*required/', $html);
|
||||
}
|
||||
|
||||
public function test_single_uses_explicit_required_prop_over_helper()
|
||||
{
|
||||
$html = $this->render([
|
||||
'name' => 'disabled_flag',
|
||||
'label' => 'Disabled',
|
||||
'item' => $this->item(),
|
||||
'required' => true,
|
||||
]);
|
||||
|
||||
$this->assertMatchesRegularExpression('/name="disabled_flag"[^>]*required/', $html);
|
||||
}
|
||||
}
|
||||
202
tests/Feature/Blade/RadioRowTest.php
Normal file
202
tests/Feature/Blade/RadioRowTest.php
Normal file
@ -0,0 +1,202 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Blade;
|
||||
|
||||
use App\Http\Middleware\CheckForDebug;
|
||||
use App\Http\Middleware\CheckForSetup;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Tests\TestCase;
|
||||
|
||||
class RadioRowTest extends TestCase
|
||||
{
|
||||
private function render(array $data, ?array $oldInput = null): string
|
||||
{
|
||||
Route::get('/__test/radio-row', function () use ($data) {
|
||||
return view('blade.form.radio-row', $data);
|
||||
});
|
||||
|
||||
$call = $this->withoutMiddleware([
|
||||
CheckForSetup::class,
|
||||
CheckForDebug::class,
|
||||
]);
|
||||
if ($oldInput !== null) {
|
||||
$call = $call->withSession(['_old_input' => $oldInput]);
|
||||
}
|
||||
|
||||
return $call->get('/__test/radio-row')->assertOk()->getContent();
|
||||
}
|
||||
|
||||
private function item(): object
|
||||
{
|
||||
return new class
|
||||
{
|
||||
public string $admin_cc_always = '1';
|
||||
|
||||
public ?string $preference = null;
|
||||
|
||||
public static function rules()
|
||||
{
|
||||
return ['admin_cc_always' => 'required'];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public function test_fresh_render_checks_the_matching_model_value()
|
||||
{
|
||||
$html = $this->render([
|
||||
'name' => 'admin_cc_always',
|
||||
'label' => 'CC Admin',
|
||||
'options' => ['1' => 'Always', '0' => 'Never'],
|
||||
'item' => $this->item(),
|
||||
]);
|
||||
|
||||
$this->assertMatchesRegularExpression('/value="1"[^>]*checked/', $html);
|
||||
$this->assertDoesNotMatchRegularExpression('/value="0"[^>]*checked/', $html);
|
||||
}
|
||||
|
||||
public function test_fresh_render_with_null_model_value_checks_nothing()
|
||||
{
|
||||
$html = $this->render([
|
||||
'name' => 'preference',
|
||||
'label' => 'Preference',
|
||||
'options' => ['a' => 'A', 'b' => 'B', 'c' => 'C'],
|
||||
'item' => $this->item(),
|
||||
]);
|
||||
|
||||
$this->assertDoesNotMatchRegularExpression('/value="a"[^>]*checked/', $html);
|
||||
$this->assertDoesNotMatchRegularExpression('/value="b"[^>]*checked/', $html);
|
||||
$this->assertDoesNotMatchRegularExpression('/value="c"[^>]*checked/', $html);
|
||||
}
|
||||
|
||||
public function test_explicit_selected_overrides_model_value()
|
||||
{
|
||||
$html = $this->render([
|
||||
'name' => 'admin_cc_always',
|
||||
'label' => 'CC Admin',
|
||||
'options' => ['1' => 'Always', '0' => 'Never'],
|
||||
'item' => $this->item(),
|
||||
'selected' => '0',
|
||||
]);
|
||||
|
||||
$this->assertMatchesRegularExpression('/value="0"[^>]*checked/', $html);
|
||||
$this->assertDoesNotMatchRegularExpression('/value="1"[^>]*checked/', $html);
|
||||
}
|
||||
|
||||
public function test_redisplay_reflects_old_value_not_model()
|
||||
{
|
||||
// Model says 1; user submitted 0 and validation failed elsewhere.
|
||||
// Old input has admin_cc_always=0. Redisplay must reflect that.
|
||||
$html = $this->render(
|
||||
data: [
|
||||
'name' => 'admin_cc_always',
|
||||
'label' => 'CC Admin',
|
||||
'options' => ['1' => 'Always', '0' => 'Never'],
|
||||
'item' => $this->item(),
|
||||
],
|
||||
oldInput: ['admin_cc_always' => '0'],
|
||||
);
|
||||
|
||||
$this->assertMatchesRegularExpression('/value="0"[^>]*checked/', $html);
|
||||
$this->assertDoesNotMatchRegularExpression('/value="1"[^>]*checked/', $html);
|
||||
}
|
||||
|
||||
public function test_redisplay_with_no_old_value_does_not_fall_back_to_model()
|
||||
{
|
||||
// Session has old input but this specific field is absent. Radios
|
||||
// should render nothing checked; they must NOT fall through to the
|
||||
// stale $item->admin_cc_always default (which would mislead the
|
||||
// user about what they submitted).
|
||||
$html = $this->render(
|
||||
data: [
|
||||
'name' => 'admin_cc_always',
|
||||
'label' => 'CC Admin',
|
||||
'options' => ['1' => 'Always', '0' => 'Never'],
|
||||
'item' => $this->item(),
|
||||
],
|
||||
oldInput: ['some_other_field' => 'x'],
|
||||
);
|
||||
|
||||
$this->assertDoesNotMatchRegularExpression('/value="1"[^>]*checked/', $html);
|
||||
$this->assertDoesNotMatchRegularExpression('/value="0"[^>]*checked/', $html);
|
||||
}
|
||||
|
||||
public function test_no_label_uses_offset_column_default()
|
||||
{
|
||||
$html = $this->render([
|
||||
'name' => 'admin_cc_always',
|
||||
'options' => ['1' => 'Always', '0' => 'Never'],
|
||||
'item' => $this->item(),
|
||||
]);
|
||||
|
||||
$this->assertStringContainsString('col-md-8 col-md-offset-3', $html);
|
||||
}
|
||||
|
||||
public function test_with_label_uses_non_offset_column_default()
|
||||
{
|
||||
$html = $this->render([
|
||||
'name' => 'admin_cc_always',
|
||||
'label' => 'CC Admin',
|
||||
'options' => ['1' => 'Always', '0' => 'Never'],
|
||||
'item' => $this->item(),
|
||||
]);
|
||||
|
||||
// With a left-hand label the option column shouldn't have the
|
||||
// col-md-offset-3 push (that offset conflicts with the col-md-3
|
||||
// label the row already emits).
|
||||
preg_match_all('/class="col-md-8[^"]*"/', $html, $matches);
|
||||
$classes = $matches[0] ?? [];
|
||||
$this->assertNotEmpty($classes);
|
||||
foreach ($classes as $class) {
|
||||
// The @error and help_text blocks legitimately use offset even
|
||||
// when there's a label, so we only care about the primary
|
||||
// options container: at least one col-md-8 that isn't offset.
|
||||
if (! str_contains($class, 'col-md-offset-3')) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
$this->fail('Expected at least one non-offset col-md-8 option container.');
|
||||
}
|
||||
|
||||
public function test_required_is_applied_to_first_radio_only()
|
||||
{
|
||||
$html = $this->render([
|
||||
'name' => 'admin_cc_always',
|
||||
'label' => 'CC Admin',
|
||||
'options' => ['1' => 'Always', '0' => 'Never'],
|
||||
'item' => $this->item(),
|
||||
]);
|
||||
|
||||
// Browser treats any-required-in-a-radio-group as "the group is
|
||||
// required," so we emit required on the first option only.
|
||||
$this->assertMatchesRegularExpression('/value="1"[^>]*required/', $html);
|
||||
$this->assertDoesNotMatchRegularExpression('/value="0"[^>]*required/', $html);
|
||||
}
|
||||
|
||||
public function test_no_item_does_not_crash_when_deriving_required()
|
||||
{
|
||||
// Transient forms like bulk-checkin have no persistent model backing.
|
||||
// The row must not try to dereference a null $item via checkIfRequired.
|
||||
$html = $this->render([
|
||||
'name' => 'update_default_location',
|
||||
'selected' => '1',
|
||||
'options' => ['1' => 'Update default', '0' => 'Leave alone'],
|
||||
]);
|
||||
|
||||
$this->assertMatchesRegularExpression('/value="1"[^>]*checked/', $html);
|
||||
$this->assertDoesNotMatchRegularExpression('/value="1"[^>]*required/', $html);
|
||||
}
|
||||
|
||||
public function test_disabled_is_applied_to_every_radio()
|
||||
{
|
||||
$html = $this->render([
|
||||
'name' => 'admin_cc_always',
|
||||
'label' => 'CC Admin',
|
||||
'options' => ['1' => 'Always', '0' => 'Never'],
|
||||
'item' => $this->item(),
|
||||
'disabled' => true,
|
||||
]);
|
||||
|
||||
$this->assertMatchesRegularExpression('/value="1"[^>]*disabled/', $html);
|
||||
$this->assertMatchesRegularExpression('/value="0"[^>]*disabled/', $html);
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,7 @@ namespace Tests\Unit\Rules;
|
||||
|
||||
use App\Rules\ExternalUrl;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use Tests\TestCase;
|
||||
|
||||
class ExternalUrlTest extends TestCase
|
||||
@ -60,9 +61,7 @@ class ExternalUrlTest extends TestCase
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider rejectedProvider
|
||||
*/
|
||||
#[DataProvider('rejectedProvider')]
|
||||
public function test_rejects_dangerous_or_malformed_urls(string $url)
|
||||
{
|
||||
$this->assertFalse($this->passes($url), 'Should have been rejected: '.$url);
|
||||
|
||||
Reference in New Issue
Block a user