feat(ticket): enhance ticket component with disabled and expired states, update styles and tests
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
<article class="ticket">
|
<article class="ticket" [class.ticket--disabled]="disabled()" [attr.aria-disabled]="disabled()">
|
||||||
<label class="ticket__selection">
|
<label class="ticket__selection">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@@ -13,17 +13,35 @@
|
|||||||
<p class="ticket__id">ID: {{ ticketId() }}</p>
|
<p class="ticket__id">ID: {{ ticketId() }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if (date(); as ticketDate) {
|
@if (date() || expired()) {
|
||||||
<time class="ticket__date">{{ ticketDate }}</time>
|
<div class="ticket__status">
|
||||||
|
@if (expired()) {
|
||||||
|
<span class="ticket__expired">VENCIDO</span>
|
||||||
|
}
|
||||||
|
@if (date(); as ticketDate) {
|
||||||
|
<time class="ticket__date">{{ ticketDate }}</time>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<div class="ticket__actions">
|
<div class="ticket__actions">
|
||||||
<app-button variant="primary" buttonClass="ticket__qr-button" (click)="viewQr.emit()">
|
<app-button
|
||||||
|
variant="primary"
|
||||||
|
buttonClass="ticket__qr-button"
|
||||||
|
[disabled]="disabled()"
|
||||||
|
(click)="viewQr.emit()"
|
||||||
|
>
|
||||||
<i class="fa-solid fa-qrcode" aria-hidden="true"></i>
|
<i class="fa-solid fa-qrcode" aria-hidden="true"></i>
|
||||||
<span>Ver QR</span>
|
<span>Ver QR</span>
|
||||||
</app-button>
|
</app-button>
|
||||||
|
|
||||||
<app-icon-button variant="download" ariaLabel="Descargar ticket" (clicked)="download.emit()" />
|
@if (!disabled()) {
|
||||||
<app-icon-button variant="share" ariaLabel="Compartir ticket" (clicked)="share.emit()" />
|
<app-icon-button
|
||||||
|
variant="download"
|
||||||
|
ariaLabel="Descargar ticket"
|
||||||
|
(clicked)="download.emit()"
|
||||||
|
/>
|
||||||
|
<app-icon-button variant="share" ariaLabel="Compartir ticket" (clicked)="share.emit()" />
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
|||||||
@@ -48,16 +48,30 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ticket__id {
|
.ticket__id {
|
||||||
|
margin-top: 3px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ticket__date {
|
.ticket__date {
|
||||||
|
display: block;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ticket__status {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ticket__expired {
|
||||||
|
display: block;
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1.4;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
.ticket__actions {
|
.ticket__actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -72,6 +86,17 @@
|
|||||||
padding-inline: 14px;
|
padding-inline: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ticket--disabled {
|
||||||
|
color: #8a8a8a;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .ticket--disabled button.ticket__qr-button:disabled {
|
||||||
|
--bs-btn-disabled-bg: #8a8a8a;
|
||||||
|
--bs-btn-disabled-border-color: #8a8a8a;
|
||||||
|
--bs-btn-disabled-color: #ffffff;
|
||||||
|
--bs-btn-disabled-opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.ticket__selection input:focus-visible {
|
.ticket__selection input:focus-visible {
|
||||||
outline: 2px solid var(--tenant-primary, #009933);
|
outline: 2px solid var(--tenant-primary, #009933);
|
||||||
outline-offset: 2px;
|
outline-offset: 2px;
|
||||||
@@ -83,7 +108,7 @@
|
|||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ticket__date {
|
.ticket__status {
|
||||||
grid-column: 2;
|
grid-column: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,4 +32,32 @@ describe('TicketComponent', () => {
|
|||||||
expect(download).toHaveBeenCalledOnce();
|
expect(download).toHaveBeenCalledOnce();
|
||||||
expect(share).toHaveBeenCalledOnce();
|
expect(share).toHaveBeenCalledOnce();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('renders the expired disabled state without secondary actions', async () => {
|
||||||
|
await TestBed.configureTestingModule({ imports: [TicketComponent] }).compileComponents();
|
||||||
|
|
||||||
|
const fixture = TestBed.createComponent(TicketComponent);
|
||||||
|
fixture.componentRef.setInput('ticketId', '0000000000');
|
||||||
|
fixture.componentRef.setInput('date', '09 de octubre');
|
||||||
|
fixture.componentRef.setInput('disabled', true);
|
||||||
|
fixture.componentRef.setInput('expired', true);
|
||||||
|
|
||||||
|
const viewQr = vi.fn();
|
||||||
|
fixture.componentInstance.viewQr.subscribe(viewQr);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const element = fixture.nativeElement as HTMLElement;
|
||||||
|
const ticket = element.querySelector<HTMLElement>('.ticket');
|
||||||
|
const qrButton = element.querySelector<HTMLButtonElement>('app-button button');
|
||||||
|
|
||||||
|
expect(ticket?.classList.contains('ticket--disabled')).toBe(true);
|
||||||
|
expect(ticket?.getAttribute('aria-disabled')).toBe('true');
|
||||||
|
expect(element.textContent).toContain('VENCIDO');
|
||||||
|
expect(qrButton?.disabled).toBe(true);
|
||||||
|
expect(element.querySelector('[aria-label="Descargar ticket"]')).toBeNull();
|
||||||
|
expect(element.querySelector('[aria-label="Compartir ticket"]')).toBeNull();
|
||||||
|
|
||||||
|
qrButton?.click();
|
||||||
|
expect(viewQr).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -12,8 +12,10 @@ import { IconButtonComponent } from '../../../../../../../../shared/components/i
|
|||||||
})
|
})
|
||||||
export class TicketComponent {
|
export class TicketComponent {
|
||||||
readonly title = input('ENTRADA GENERAL');
|
readonly title = input('ENTRADA GENERAL');
|
||||||
readonly ticketId = input.required<string>();
|
readonly ticketId = input.required<string | number>();
|
||||||
readonly date = input<string | null>(null);
|
readonly date = input<string | null>(null);
|
||||||
|
readonly disabled = input(false);
|
||||||
|
readonly expired = input(false);
|
||||||
readonly selected = model(false);
|
readonly selected = model(false);
|
||||||
|
|
||||||
readonly viewQr = output<void>();
|
readonly viewQr = output<void>();
|
||||||
|
|||||||
@@ -33,8 +33,10 @@
|
|||||||
@for (ticket of tickets(); track ticket.id) {
|
@for (ticket of tickets(); track ticket.id) {
|
||||||
<app-ticket
|
<app-ticket
|
||||||
[title]="ticket.name"
|
[title]="ticket.name"
|
||||||
[ticketId]="ticket.ticket"
|
[ticketId]="ticket.id"
|
||||||
[date]="formatDate(ticket)"
|
[date]="formatDate(ticket)"
|
||||||
|
[disabled]="!ticket.is_valid"
|
||||||
|
[expired]="ticket.is_expired"
|
||||||
[selected]="isSelected(ticket.id)"
|
[selected]="isSelected(ticket.id)"
|
||||||
(selectedChange)="toggleTicket(ticket.id, $event)"
|
(selectedChange)="toggleTicket(ticket.id, $event)"
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user