Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: code quality linting improvements #550

Merged
merged 5 commits into from
Jan 23, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export class CheckboxPreviewComponent {}

export const defaultCode = `
import { Component } from '@angular/core';
import { HlmCheckboxCheckIconComponent, HlmCheckboxComponent } from '@spartan-ng/ui-checkbox-helm';
import { HlmCheckboxComponent } from '@spartan-ng/ui-checkbox-helm';
import { HlmLabelDirective } from '@spartan-ng/ui-label-helm';
@Component({
selector: 'spartan-checkbox-preview',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { BrnMenuTriggerDirective } from '@spartan-ng/brain/menu';
import { BrnSelectModule } from '@spartan-ng/brain/select';
import { BrnTableModule, type PaginatorState, useBrnColumnManager } from '@spartan-ng/brain/table';
import { HlmButtonModule } from '@spartan-ng/ui-button-helm';
import { HlmCheckboxCheckIconComponent, HlmCheckboxComponent } from '@spartan-ng/ui-checkbox-helm';
import { HlmCheckboxComponent } from '@spartan-ng/ui-checkbox-helm';
import { HlmIconDirective } from '@spartan-ng/ui-icon-helm';
import { HlmInputDirective } from '@spartan-ng/ui-input-helm';
import { HlmMenuModule } from '@spartan-ng/ui-menu-helm';
Expand Down Expand Up @@ -168,7 +168,6 @@ const PAYMENT_DATA: Payment[] = [
HlmIconDirective,
HlmInputDirective,

HlmCheckboxCheckIconComponent,
HlmCheckboxComponent,

BrnSelectModule,
Expand Down Expand Up @@ -404,7 +403,7 @@ import { toObservable, toSignal } from '@angular/core/rxjs-interop';
import { FormsModule } from '@angular/forms';
import { lucideArrowUpDown, lucideChevronDown, lucideEllipsis } from '@ng-icons/lucide';
import { HlmButtonModule } from '@spartan-ng/ui-button-helm';
import { HlmCheckboxCheckIconComponent, HlmCheckboxComponent } from '@spartan-ng/ui-checkbox-helm';
import { HlmCheckboxComponent } from '@spartan-ng/ui-checkbox-helm';
import { HlmIconDirective, provideIcons } from '@spartan-ng/ui-icon-helm';
import { HlmInputDirective } from '@spartan-ng/ui-input-helm';
import { BrnMenuTriggerDirective } from '@spartan-ng/brain/menu';
Expand Down Expand Up @@ -565,7 +564,6 @@ const PAYMENT_DATA: Payment[] = [
HlmIconDirective,
HlmInputDirective,

HlmCheckboxCheckIconComponent,
HlmCheckboxComponent,

BrnSelectModule,
Expand Down
62 changes: 27 additions & 35 deletions libs/ui/breadcrumb/helm/eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,42 +1,34 @@
const { FlatCompat } = require('@eslint/eslintrc');
const nx = require('@nx/eslint-plugin');
const baseConfig = require('../../../../eslint.config.cjs');
const js = require('@eslint/js');

const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
});

module.exports = [
...baseConfig,
...compat
.config({ extends: ['plugin:@nx/angular', 'plugin:@angular-eslint/template/process-inline-templates'] })
.map((config) => ({
...config,
files: ['**/*.ts'],
rules: {
'@angular-eslint/no-host-metadata-property': 0,
'@angular-eslint/directive-selector': [
'error',
{
type: 'attribute',
prefix: 'hlm',
style: 'camelCase',
},
],
'@angular-eslint/component-selector': [
'error',
{
type: 'element',
prefix: 'hlm',
style: 'kebab-case',
},
],
},
})),
...compat.config({ extends: ['plugin:@nx/angular-template'] }).map((config) => ({
...config,
...nx.configs['flat/angular'],
...nx.configs['flat/angular-template'],
{
files: ['**/*.ts'],
rules: {
'@angular-eslint/directive-selector': [
'error',
{
type: 'attribute',
prefix: 'hlm',
style: 'camelCase',
},
],
'@angular-eslint/component-selector': [
'error',
{
type: 'element',
prefix: 'hlm',
style: 'kebab-case',
},
],
},
},
{
files: ['**/*.html'],
// Override or add rules here
rules: {},
})),
},
];
2 changes: 1 addition & 1 deletion libs/ui/carousel/helm/eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ module.exports = [
'@angular-eslint/component-selector': [
'error',
{
type: 'element',
type: ['attribute', 'element'],
prefix: 'hlm',
style: 'kebab-case',
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import type { ClassValue } from 'clsx';
import { HlmCarouselComponent } from './hlm-carousel.component';

@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'button[hlm-carousel-next], button[hlmCarouselNext]',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import type { ClassValue } from 'clsx';
import { HlmCarouselComponent } from './hlm-carousel.component';

@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'button[hlm-carousel-previous], button[hlmCarouselPrevious]',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
Expand Down
4 changes: 1 addition & 3 deletions libs/ui/checkbox/helm/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { NgModule } from '@angular/core';

import { HlmCheckboxCheckIconComponent } from './lib/hlm-checkbox-checkicon.component';
import { HlmCheckboxComponent } from './lib/hlm-checkbox.component';

export * from './lib/hlm-checkbox-checkicon.component';
export * from './lib/hlm-checkbox.component';

export const HlmCheckboxImports = [HlmCheckboxComponent, HlmCheckboxCheckIconComponent] as const;
export const HlmCheckboxImports = [HlmCheckboxComponent] as const;
@NgModule({
imports: [...HlmCheckboxImports],
exports: [...HlmCheckboxImports],
Expand Down
35 changes: 0 additions & 35 deletions libs/ui/checkbox/helm/src/lib/hlm-checkbox-checkicon.component.ts

This file was deleted.

51 changes: 29 additions & 22 deletions libs/ui/checkbox/helm/src/lib/hlm-checkbox.component.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { Component, booleanAttribute, computed, forwardRef, input, model, output, signal } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { NgIcon, provideIcons } from '@ng-icons/core';
import { lucideCheck } from '@ng-icons/lucide';
import { BrnCheckboxComponent } from '@spartan-ng/brain/checkbox';
import { hlm } from '@spartan-ng/brain/core';
import { ChangeFn, TouchFn } from '@spartan-ng/brain/forms';
ashley-hunter marked this conversation as resolved.
Show resolved Hide resolved
import { HlmIconDirective } from '@spartan-ng/ui-icon-helm';
import type { ClassValue } from 'clsx';
import { HlmCheckboxCheckIconComponent } from './hlm-checkbox-checkicon.component';

export const HLM_CHECKBOX_VALUE_ACCESSOR = {
provide: NG_VALUE_ACCESSOR,
Expand All @@ -14,7 +17,7 @@ export const HLM_CHECKBOX_VALUE_ACCESSOR = {
@Component({
selector: 'hlm-checkbox',
standalone: true,
imports: [BrnCheckboxComponent, HlmCheckboxCheckIconComponent],
imports: [BrnCheckboxComponent, NgIcon, HlmIconDirective],
template: `
<brn-checkbox
[id]="id()"
Expand All @@ -27,9 +30,9 @@ export const HLM_CHECKBOX_VALUE_ACCESSOR = {
[aria-labelledby]="ariaLabelledby()"
[aria-describedby]="ariaDescribedby()"
(changed)="_handleChange()"
(touched)="_onTouched()"
(touched)="_onTouched?.()"
>
<hlm-checkbox-checkicon [class]="checkIconClass()" [iconName]="checkIconName()" />
<ng-icon hlm size="sm" name="lucideCheck" />
Copy link
Collaborator

@elite-benni elite-benni Jan 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name should come from the input, some users want to use another icon.
I see that you removed the input. I would say this is a breaking change.
Would you say, if someone wants another icon he should change the hlm source in his repo?
This also applies to the class

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, so I guess the question is, where does it make sense for this kind of customization? Is there really a common case for checkboxes in different parts of an app to have a different check icons?
In most cases that I have seen e.g. material, shadcn etc.. the icon is always the same for every checkbox.

I can see a case where a user may want a different icon for every checkbox, in which case customizing the helm component makes perfect sense. But does it make sense to also change it on a per instance basis?

If so I can certainly add it back in, happy to hear thoughts!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I cannot remember why the inputs were there in the first place.
I just wanted to point out that some users could maybe used this feature.
Just an example that pops into my head. Someone used checkbox in a tic tac toe game. Maybe not the best example. 😉 But we don't know what users are coming up with when using the library.

In an normal app i totally agree with you, that checkboxes should always look the same, and maybe these special cases should not be handled with a checkbox.

Both ways have their valid points and I have a hard time to decide which one is better.

Just another question. Who provides the icon now, that was previously done by the hlmcheckicon component, which also was a little bit weird if someone used his own icon.
So maybe the cleaner end result is to remove it and document it as breaking change.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The helm checkbox icon component was only used internally so never something that would have been used by a consumer.
The checkbox component now provides the icon directly.
But yes in the case where someone was to provide a custom icon they would have to provide it manually themselves.

I'm happy to restore it if we think it's worth it, just not something any other library I see provides out of the box and most libraries don't copy the components into the project, we do, so that kind of infrequent thing could be added in the project if someone needed it, rather than us needing to provide it by default for everyone, but I can easily restore it if we think it's best.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh i missed that!

It is fine for me, if someone needs the inputs he still has the possibility to do this in his own helm component.

I believe it is the cleaner way, but it has the possibility to be a breaking change.

Nice work

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Grand, I have updated the PR description to mention this. If anyone else has an opinion let me know, happy to revert this if needed. Thanks for you feedback!

</brn-checkbox>
`,
host: {
Expand All @@ -40,10 +43,12 @@ export const HLM_CHECKBOX_VALUE_ACCESSOR = {
'[attr.aria-describedby]': 'null',
},
providers: [HLM_CHECKBOX_VALUE_ACCESSOR],
viewProviders: [provideIcons({ lucideCheck })],
})
export class HlmCheckboxComponent {
public readonly userClass = input<ClassValue>('', { alias: 'class' });
protected _computedClass = computed(() =>

protected readonly _computedClass = computed(() =>
hlm(
'group inline-flex border border-foreground shrink-0 cursor-pointer items-center rounded-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring' +
' focus-visible:ring-offset-2 focus-visible:ring-offset-background data-[state=checked]:text-background data-[state=checked]:bg-primary data-[state=unchecked]:bg-background',
Expand All @@ -52,6 +57,10 @@ export class HlmCheckboxComponent {
),
);

protected readonly _computedIconClass = computed(() =>
hlm('leading-none group-data-[state=unchecked]:opacity-0', this.checked() === 'indeterminate' ? 'opacity-50' : ''),
);

/** Used to set the id on the underlying brn element. */
public readonly id = input<string | null>(null);

Expand All @@ -64,54 +73,52 @@ export class HlmCheckboxComponent {
/** Used to set the aria-describedby attribute on the underlying brn element. */
public readonly ariaDescribedby = input<string | null>(null, { alias: 'aria-describedby' });

public readonly checked = model<boolean | 'indeterminate'>(false);
/** The checked state of the checkbox. */
public readonly checked = model<CheckboxValue>(false);

/** The name attribute of the checkbox. */
public readonly name = input<string | null>(null);

/** Whether the checkbox is required. */
public readonly required = input(false, { transform: booleanAttribute });

/** Whether the checkbox is disabled. */
public readonly disabled = input(false, { transform: booleanAttribute });

protected readonly state = computed(() => ({
disabled: signal(this.disabled()),
}));

// icon inputs
public readonly checkIconName = input<string>('lucideCheck');
public readonly checkIconClass = input<ClassValue>('');

public readonly changed = output<boolean>();

protected _onChange?: ChangeFn<CheckboxValue>;
protected _onTouched?: TouchFn;

protected _handleChange(): void {
if (this.state().disabled()) return;

const previousChecked = this.checked();
this.checked.set(previousChecked === 'indeterminate' ? true : !previousChecked);
this._onChange(!previousChecked);
this._onChange?.(!previousChecked);
this.changed.emit(!previousChecked);
}

/** CONROL VALUE ACCESSOR */

// eslint-disable-next-line @typescript-eslint/no-explicit-any
writeValue(value: any): void {
writeValue(value: CheckboxValue): void {
this.checked.set(!!value);
}
// eslint-disable-next-line @typescript-eslint/no-empty-function,,@typescript-eslint/no-explicit-any
protected _onChange = (_: any) => {};
// eslint-disable-next-line @typescript-eslint/no-empty-function
protected _onTouched = () => {};

// eslint-disable-next-line @typescript-eslint/no-explicit-any
registerOnChange(fn: any): void {
registerOnChange(fn: ChangeFn<CheckboxValue>): void {
this._onChange = fn;
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
registerOnTouched(fn: any): void {
registerOnTouched(fn: TouchFn): void {
this._onTouched = fn;
}

setDisabledState(isDisabled: boolean): void {
this.state().disabled.set(isDisabled);
}
}

type CheckboxValue = boolean | 'indeterminate';
5 changes: 2 additions & 3 deletions libs/ui/dialog/helm/src/lib/hlm-dialog.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ import {
import { HlmDialogContentComponent } from './hlm-dialog-content.component';
import { hlmDialogOverlayClass } from './hlm-dialog-overlay.directive';

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type HlmDialogOptions<DialogContext = any> = BrnDialogOptions & {
export type HlmDialogOptions<DialogContext = unknown> = BrnDialogOptions & {
contentClass?: string;
context?: DialogContext;
};
Expand All @@ -28,7 +27,7 @@ export class HlmDialogService {

...(options ?? {}),
backdropClass: cssClassesToArray(`${hlmDialogOverlayClass} ${options?.backdropClass ?? ''}`),
context: { ...options?.context, $component: component, $dynamicComponentClass: options?.contentClass },
context: { ...(options?.context ?? {}), $component: component, $dynamicComponentClass: options?.contentClass },
};

return this._brnDialogService.open(HlmDialogContentComponent, undefined, mergedOptions.context, mergedOptions);
Expand Down
7 changes: 1 addition & 6 deletions libs/ui/icon/helm/src/lib/hlm-icon.directive.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
import { Directive, computed, input } from '@angular/core';
import { injectHlmIconConfig } from './hlm-icon.token';

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const DEFINED_SIZES = ['xs', 'sm', 'base', 'lg', 'xl', 'none'] as const;

type DefinedSizes = (typeof DEFINED_SIZES)[number];

export type IconSize = DefinedSizes | (Record<never, never> & string);
export type IconSize = 'xs' | 'sm' | 'base' | 'lg' | 'xl' | 'none' | ({} & string);
ashley-hunter marked this conversation as resolved.
Show resolved Hide resolved

@Directive({
selector: 'ng-icon[hlm]',
Expand Down
1 change: 0 additions & 1 deletion libs/ui/sonner/helm/jest.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable */
export default {
displayName: 'ui-sonner-helm',
preset: '../../../../jest.preset.cjs',
Expand Down
13 changes: 7 additions & 6 deletions libs/ui/switch/helm/src/lib/hlm-switch.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const HLM_SWITCH_VALUE_ACCESSOR = {
[class]="_computedClass()"
[checked]="checked()"
(changed)="handleChange($event)"
(touched)="_onTouched()"
(touched)="_onTouched?.()"
[disabled]="disabled()"
[id]="id()"
[aria-label]="ariaLabel()"
Expand All @@ -50,8 +50,10 @@ export class HlmSwitchComponent {
),
);

/** The checked state of the switch. */
public readonly checked = model<boolean>(false);

/** The disabled state of the switch. */
public readonly disabled = input<boolean, BooleanInput>(false, {
transform: booleanAttribute,
});
Expand All @@ -68,16 +70,15 @@ export class HlmSwitchComponent {
/** Used to set the aria-describedby attribute on the underlying brn element. */
public readonly ariaDescribedby = input<string | null>(null, { alias: 'aria-describedby' });

/** Emits when the checked state of the switch changes. */
public readonly changed = output<boolean>();

// eslint-disable-next-line @typescript-eslint/no-empty-function
protected _onChange: ChangeFn<boolean> = () => {};
// eslint-disable-next-line @typescript-eslint/no-empty-function
protected _onTouched: TouchFn = () => {};
protected _onChange?: ChangeFn<boolean>;
protected _onTouched?: TouchFn;

protected handleChange(value: boolean): void {
this.checked.set(value);
this._onChange(value);
this._onChange?.(value);
this.changed.emit(value);
}

Expand Down
Loading