feat(forms): make textarea monospace opt-in and improve multiline toggle
Add `monospace` prop to Textarea component so font-mono is no longer applied by default. Apply it explicitly to env variable editors, private key fields, and shared variable forms where monospace is appropriate. Use Alpine.js x-data/x-model to make the multiline toggle reactive without a full Livewire round-trip. Add wire:key on the input/textarea wrappers to force proper DOM replacement when switching modes.
This commit is contained in:
parent
47025c7815
commit
3961077b90
12 changed files with 87 additions and 35 deletions
|
|
@ -32,10 +32,11 @@ public function __construct(
|
|||
public bool $allowTab = false,
|
||||
public bool $spellcheck = false,
|
||||
public bool $autofocus = false,
|
||||
public bool $monospace = false,
|
||||
public ?string $helper = null,
|
||||
public bool $realtimeValidation = false,
|
||||
public bool $allowToPeak = true,
|
||||
public string $defaultClass = 'input scrollbar font-mono',
|
||||
public string $defaultClass = 'input scrollbar',
|
||||
public string $defaultClassInput = 'input',
|
||||
public ?int $minlength = null,
|
||||
public ?int $maxlength = null,
|
||||
|
|
@ -81,6 +82,10 @@ public function render(): View|Closure|string
|
|||
$this->name = $this->modelBinding !== 'null' ? $this->modelBinding : (string) $this->id;
|
||||
}
|
||||
|
||||
if ($this->monospace) {
|
||||
$this->defaultClass .= ' font-mono';
|
||||
}
|
||||
|
||||
// $this->label = Str::title($this->label);
|
||||
return view('components.forms.textarea');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
@theme {
|
||||
--font-sans: 'Geist Sans', Inter, sans-serif;
|
||||
--font-mono: 'Geist Mono', 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
|
||||
--font-geist-sans: 'Geist Sans', Inter, sans-serif;
|
||||
--font-logs: 'Geist Mono', 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +1,23 @@
|
|||
<form class="flex flex-col w-full gap-2 rounded-sm" wire:submit='submit'>
|
||||
<form class="flex flex-col w-full gap-2 rounded-sm" wire:submit='submit'
|
||||
x-data="{ isMultiline: $wire.entangle('is_multiline') }">
|
||||
<x-forms.input placeholder="NODE_ENV" id="key" label="Name" required />
|
||||
@if ($is_multiline)
|
||||
<x-forms.textarea id="value" label="Value" required />
|
||||
@else
|
||||
<x-forms.env-var-input placeholder="production" id="value" label="Value" required
|
||||
:availableVars="$shared ? [] : $this->availableSharedVariables"
|
||||
:projectUuid="data_get($parameters, 'project_uuid')"
|
||||
:environmentUuid="data_get($parameters, 'environment_uuid')"
|
||||
:serverUuid="data_get($parameters, 'server_uuid')" />
|
||||
@endif
|
||||
<template x-if="isMultiline">
|
||||
<div wire:key="env-value-textarea">
|
||||
<x-forms.textarea id="value" label="Value" required class="font-sans" spellcheck />
|
||||
</div>
|
||||
</template>
|
||||
<template x-if="!isMultiline">
|
||||
<div wire:key="env-value-input">
|
||||
<x-forms.env-var-input placeholder="production" id="value" label="Value" required
|
||||
:availableVars="$shared ? [] : $this->availableSharedVariables"
|
||||
:projectUuid="data_get($parameters, 'project_uuid')"
|
||||
:environmentUuid="data_get($parameters, 'environment_uuid')"
|
||||
:serverUuid="data_get($parameters, 'server_uuid')" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@if (!$shared && !$is_multiline)
|
||||
<div class="text-xs text-neutral-500 dark:text-neutral-400 -mt-1">
|
||||
@if (!$shared)
|
||||
<div x-cloak x-show="!isMultiline" wire:key="env-value-tip" class="text-xs text-neutral-500 dark:text-neutral-400 -mt-1">
|
||||
Tip: Type <span class="font-mono dark:text-warning text-coollabs">{{</span> to reference a shared environment
|
||||
variable
|
||||
</div>
|
||||
|
|
@ -34,8 +40,8 @@
|
|||
label="Is Literal?" />
|
||||
@endif
|
||||
|
||||
<x-forms.checkbox id="is_multiline" label="Is Multiline?" />
|
||||
<x-forms.checkbox id="is_multiline" x-model="isMultiline" label="Is Multiline?" />
|
||||
<x-forms.button type="submit" @click="slideOverOpen=false">
|
||||
Save
|
||||
</x-forms.button>
|
||||
</form>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -84,24 +84,24 @@
|
|||
Inline comments with space before # (e.g., <code class="font-mono">KEY=value #comment</code>) are stripped.
|
||||
</x-callout>
|
||||
|
||||
<x-forms.textarea rows="10" class="whitespace-pre-wrap" id="variables" wire:model="variables"
|
||||
<x-forms.textarea rows="10" class="whitespace-pre-wrap" id="variables" wire:model="variables" monospace
|
||||
label="Production Environment Variables"></x-forms.textarea>
|
||||
|
||||
@if ($showPreview)
|
||||
<x-forms.textarea rows="10" class="whitespace-pre-wrap" label="Preview Deployments Environment Variables"
|
||||
<x-forms.textarea rows="10" class="whitespace-pre-wrap" label="Preview Deployments Environment Variables" monospace
|
||||
id="variablesPreview" wire:model="variablesPreview"></x-forms.textarea>
|
||||
@endif
|
||||
|
||||
<x-forms.button type="submit" class="btn btn-primary">Save All Environment Variables</x-forms.button>
|
||||
@else
|
||||
<x-forms.textarea rows="10" class="whitespace-pre-wrap" id="variables" wire:model="variables"
|
||||
<x-forms.textarea rows="10" class="whitespace-pre-wrap" id="variables" wire:model="variables" monospace
|
||||
label="Production Environment Variables" disabled></x-forms.textarea>
|
||||
|
||||
@if ($showPreview)
|
||||
<x-forms.textarea rows="10" class="whitespace-pre-wrap" label="Preview Deployments Environment Variables"
|
||||
<x-forms.textarea rows="10" class="whitespace-pre-wrap" label="Preview Deployments Environment Variables" monospace
|
||||
id="variablesPreview" wire:model="variablesPreview" disabled></x-forms.textarea>
|
||||
@endif
|
||||
@endcan
|
||||
</form>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -150,17 +150,21 @@
|
|||
<div class="flex flex-col w-full gap-2 lg:flex-row">
|
||||
@if ($is_multiline)
|
||||
<x-forms.input :required="$is_redis_credential" isMultiline="{{ $is_multiline }}" id="key" />
|
||||
<x-forms.textarea :required="$is_redis_credential" type="password" id="value" />
|
||||
<div class="flex-1" wire:key="env-show-value-textarea-{{ $env->id }}">
|
||||
<x-forms.textarea :required="$is_redis_credential" type="password" id="value" />
|
||||
</div>
|
||||
@else
|
||||
<x-forms.input :disabled="$is_redis_credential" :required="$is_redis_credential" id="key" />
|
||||
<x-forms.env-var-input
|
||||
:required="$is_redis_credential"
|
||||
type="password"
|
||||
id="value"
|
||||
:availableVars="$isSharedVariable ? [] : $this->availableSharedVariables"
|
||||
:projectUuid="data_get($parameters, 'project_uuid')"
|
||||
:environmentUuid="data_get($parameters, 'environment_uuid')"
|
||||
:serverUuid="data_get($parameters, 'server_uuid')" />
|
||||
<div class="flex-1" wire:key="env-show-value-input-{{ $env->id }}">
|
||||
<x-forms.env-var-input
|
||||
:required="$is_redis_credential"
|
||||
type="password"
|
||||
id="value"
|
||||
:availableVars="$isSharedVariable ? [] : $this->availableSharedVariables"
|
||||
:projectUuid="data_get($parameters, 'project_uuid')"
|
||||
:environmentUuid="data_get($parameters, 'environment_uuid')"
|
||||
:serverUuid="data_get($parameters, 'server_uuid')" />
|
||||
</div>
|
||||
@endif
|
||||
@if ($is_shared)
|
||||
<x-forms.input :disabled="$is_redis_credential" :required="$is_redis_credential" disabled
|
||||
|
|
@ -312,4 +316,4 @@
|
|||
@endif
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<x-forms.input id="name" label="Name" required />
|
||||
<x-forms.input id="description" label="Description" />
|
||||
</div>
|
||||
<x-forms.textarea realtimeValidation id="value" rows="10"
|
||||
<x-forms.textarea realtimeValidation id="value" rows="10" monospace
|
||||
placeholder="-----BEGIN OPENSSH PRIVATE KEY-----" label="Private Key" required />
|
||||
<x-forms.input id="publicKey" readonly label="Public Key" />
|
||||
<span class="pt-2 pb-4 font-bold dark:text-warning">ACTION REQUIRED: Copy the 'Public Key' to your server's
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@
|
|||
required disabled />
|
||||
</div>
|
||||
<div x-cloak x-show="showPrivateKey">
|
||||
<x-forms.textarea canGate="update" :canResource="$private_key" rows="10" id="privateKeyValue" required />
|
||||
<x-forms.textarea canGate="update" :canResource="$private_key" rows="10" id="privateKeyValue" required monospace />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class="dark:text-warning text-coollabs">@{{ environment.VARIABLENAME }}</span><x
|
|||
</div>
|
||||
@else
|
||||
<form wire:submit='submit' class="flex flex-col gap-2">
|
||||
<x-forms.textarea canGate="update" :canResource="$environment" rows="20" class="whitespace-pre-wrap" id="variables" wire:model="variables"
|
||||
<x-forms.textarea canGate="update" :canResource="$environment" rows="20" class="whitespace-pre-wrap" id="variables" wire:model="variables" monospace
|
||||
label="Environment Shared Variables"></x-forms.textarea>
|
||||
<x-forms.button canGate="update" :canResource="$environment" type="submit" class="btn btn-primary">Save All Environment Variables</x-forms.button>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
</div>
|
||||
@else
|
||||
<form wire:submit='submit' class="flex flex-col gap-2">
|
||||
<x-forms.textarea canGate="update" :canResource="$project" rows="20" class="whitespace-pre-wrap" id="variables" wire:model="variables"
|
||||
<x-forms.textarea canGate="update" :canResource="$project" rows="20" class="whitespace-pre-wrap" id="variables" wire:model="variables" monospace
|
||||
label="Project Shared Variables"></x-forms.textarea>
|
||||
<x-forms.button canGate="update" :canResource="$project" type="submit" class="btn btn-primary">Save All Environment Variables</x-forms.button>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class="dark:text-warning text-coollabs">@{{ team.VARIABLENAME }}</span> <x-helpe
|
|||
</div>
|
||||
@else
|
||||
<form wire:submit='submit' class="flex flex-col gap-2">
|
||||
<x-forms.textarea canGate="update" :canResource="$team" rows="20" class="whitespace-pre-wrap" id="variables" wire:model="variables"
|
||||
<x-forms.textarea canGate="update" :canResource="$team" rows="20" class="whitespace-pre-wrap" id="variables" wire:model="variables" monospace
|
||||
label="Team Shared Variables"></x-forms.textarea>
|
||||
<x-forms.button canGate="update" :canResource="$team" type="submit" class="btn btn-primary">Save All Environment Variables</x-forms.button>
|
||||
</form>
|
||||
|
|
|
|||
22
tests/Feature/EnvironmentVariableMultilineToggleViewTest.php
Normal file
22
tests/Feature/EnvironmentVariableMultilineToggleViewTest.php
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
it('uses Alpine entangle to switch add value field immediately when multiline is enabled', function () {
|
||||
$view = file_get_contents(resource_path('views/livewire/project/shared/environment-variable/add.blade.php'));
|
||||
|
||||
expect($view)
|
||||
->toContain('x-data="{ isMultiline: $wire.entangle(\'is_multiline\') }"')
|
||||
->toContain('<template x-if="isMultiline">')
|
||||
->toContain('<template x-if="!isMultiline">')
|
||||
->toContain('x-model="isMultiline"')
|
||||
->toContain('<x-forms.textarea id="value" label="Value" required class="font-sans" spellcheck />')
|
||||
->toContain('wire:key="env-value-textarea"')
|
||||
->toContain('wire:key="env-value-input"');
|
||||
});
|
||||
|
||||
it('uses distinct keyed branches for the edit value field modes', function () {
|
||||
$view = file_get_contents(resource_path('views/livewire/project/shared/environment-variable/show.blade.php'));
|
||||
|
||||
expect($view)
|
||||
->toContain('wire:key="env-show-value-textarea-{{ $env->id }}"')
|
||||
->toContain('wire:key="env-show-value-input-{{ $env->id }}"');
|
||||
});
|
||||
|
|
@ -31,6 +31,20 @@
|
|||
->not->toContain('changePasswordFieldType');
|
||||
});
|
||||
|
||||
it('renders textarea without monospace classes by default', function () {
|
||||
$html = Blade::render('<x-forms.textarea id="notes" />');
|
||||
|
||||
expect($html)
|
||||
->toContain('class="input scrollbar"')
|
||||
->not->toContain('font-mono');
|
||||
});
|
||||
|
||||
it('renders textarea with monospace classes when requested', function () {
|
||||
$html = Blade::render('<x-forms.textarea id="variables" monospace />');
|
||||
|
||||
expect($html)->toContain('class="input scrollbar font-mono"');
|
||||
});
|
||||
|
||||
it('resets password visibility on success event for env-var-input', function () {
|
||||
$html = Blade::render('<x-forms.env-var-input type="password" id="secret" />');
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue