Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
2c992a0
feat(checkbox): changes from review + added card variant #222
Mar 26, 2026
529e14d
feat(checkbox): removed comments #222
Mar 26, 2026
7b6806b
feat(checkbox): fixes from coderabbit review #222
Mar 26, 2026
2e94ffc
feat(radio): new TEDI-ready component #7
Mar 27, 2026
4505c0c
feat(radio, checkbox): marked community radio and checkbox deprecated #7
Mar 27, 2026
ef1d2ec
feat(checkbox): changes from design review #222
Apr 2, 2026
beb3fe3
feat(checkbox): fixed story layout, added correct height #222
Apr 2, 2026
dfc7bd9
feat(checkbox): merged checkbox changes #222
Apr 6, 2026
a3f50a9
Merge remote-tracking branch 'origin/rc' into feat/222-checkbox-tedi-…
Apr 6, 2026
8b8ef85
Merge branch 'feat/222-checkbox-tedi-ready' into feat/7-radio-tedi-ready
Apr 6, 2026
3b92e3c
feat(filter,status-indicator): new filter component, status-indicator…
Apr 7, 2026
716e29a
feat(filter): removed unlisted components from export #329
Apr 7, 2026
d41718c
feat(filter): code review fixes #329
Apr 7, 2026
da2f982
feat(filter): updated stories, fixed styles #329
Apr 8, 2026
f8693b1
Merge remote-tracking branch 'origin/rc' into feat/329-filter-tedi-ready
mart-sessman Apr 13, 2026
f100d83
feat(filter): fixed linter error #329
mart-sessman Apr 13, 2026
8b28626
feat(filter): changes from figma, added group CVA #329
mart-sessman Apr 17, 2026
c4dfa50
Merge branch 'rc' into feat/329-filter-tedi-ready
mart-sessman May 7, 2026
6bfc42c
feat(filter): updated css variables #329
mart-sessman May 7, 2026
918bb53
feat(filter): code review changes #329
mart-sessman May 8, 2026
51deeca
chore: updated filter story with figma link, added notes to contribut…
mart-sessman May 14, 2026
03198a3
feat(filter): code review changes #329
mart-sessman May 20, 2026
9f1d6f5
feat(filter): code review changes #329
mart-sessman May 20, 2026
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
44 changes: 43 additions & 1 deletion .claude/skills/contributing/references/best-practices.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,32 @@ tedi-modal-header { padding: var(--token); }
cdk-dialog-container { outline: none; }
```

### Icon Color Inheritance Inside Parent Components

When a `tedi-icon` is placed inside an interactive component whose text color changes on hover/active/selected states, the icon should:
- **Default state:** respect the user's `color` input (e.g., `color="danger"` shows danger)
- **State changes (hover, selected, active):** inherit the parent's text color

**For component-owned icons** (in the component's own template), use `color="inherit"` so they always follow the parent:
```html
<tedi-icon name="check" [size]="18" color="inherit" />
```

**For projected/consumer icons** (via `ng-content`), the consumer controls the `color` input. The parent component must override icon color only on state changes via CSS:
```scss
.tedi-my-component__button:hover .tedi-icon {
color: inherit;
}

.tedi-my-component--selected .tedi-icon {
color: inherit;
}
```

This lets the consumer's chosen color (e.g., `color="brand"`) apply in the default state, while ensuring the icon follows the parent's text color on hover/selected/active. The selector `.tedi-my-component__button:hover .tedi-icon` (specificity 0,2,0+pseudo) beats `.tedi-icon--color-primary` (0,1,0).

**Every component that contains icons and changes text color on state must include these overrides.**

### Example
```scss
.tedi-button {
Expand Down Expand Up @@ -244,13 +270,29 @@ export const Default: StoryObj<ComponentNameComponent> = {
};

export const WithReactiveForms: StoryObj<ComponentNameComponent> = {
decorators: [
moduleMetadata({
imports: [MyControlComponent, ReactiveFormsModule, AlertComponent, TextComponent],
}),
],
render: () => ({
props: { control: new FormControl('') },
template: `<tedi-my-control [formControl]="control" />`,
template: `
<tedi-my-control [formControl]="control" />
<tedi-alert type="info" [showClose]="false">
<pre tedi-text modifiers="small" style="margin: 0;">{{ {
value: control.value,
touched: control.touched,
dirty: control.dirty
} | json }}</pre>
</tedi-alert>
`,
}),
};
```

> **Note:** Always display reactive form state using a `<tedi-alert type="info">` with a `<pre tedi-text modifiers="small">` block and the `json` pipe. This provides a consistent, scannable debug output across all form component stories. Import `AlertComponent` and `TextComponent` in the story's `moduleMetadata`.

### Story Coverage
Every story file must include:
- **Default** — component with default props
Expand Down
27 changes: 21 additions & 6 deletions .claude/skills/contributing/references/stories.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,8 @@ import { moduleMetadata } from '@storybook/angular';
import { ComponentName } from './index';

/**
* <a href="https://www.figma.com/design/..." target="_BLANK">Figma ↗</a>
* <a href="https://www.figma.com/design/..." target="_blank">Figma ↗</a>
*/

export default {
title: 'TEDI-Ready/Components/Category/ComponentName',
component: ComponentName,
Expand All @@ -121,15 +120,31 @@ export default {
imports: [ComponentName, /* required dependencies */],
}),
],
parameters: {
design: { type: 'figma', url: 'https://www.figma.com/...' },
},
argTypes: {
// One entry per public input
},
} as Meta<ComponentName>;
```

**Figma link (required).** Place the link as a JSDoc anchor directly above `export default` — Storybook renders it at the top of the Docs page. This is the convention used by every TEDI-Ready story. Format:

```typescript
/**
* <a href="https://www.figma.com/design/<file-id>/<file-name>?node-id=<node>&m=dev" target="_blank">Figma ↗</a>
*/
```

If the component also has a Zeroheight page, add it on the next line with `<br>`:

```typescript
/**
* <a href="https://www.figma.com/design/..." target="_blank">Figma ↗</a><br>
* <a href="https://www.tedi.ee/..." target="_blank">Zeroheight ↗</a>
*/
```

Applies equally to new components and retrofits — if an existing story lacks the link, add it.

### 5. Story Checklist

- [ ] Every Figma section has a corresponding story export, in the same order
Expand All @@ -138,7 +153,7 @@ export default {
- [ ] `Default` story has all controls wired up via `args`
- [ ] States story covers all visual states shown in Figma (default, hover, active, focus, disabled)
- [ ] Reactive forms example included if the component implements ControlValueAccessor
- [ ] Figma link is in the meta `parameters.design` and in the JSDoc comment above `export default`
- [ ] Figma link is in the JSDoc comment above `export default` (format: `<a href="..." target="_blank">Figma ↗</a>`)

### 6. argTypes Convention

Expand Down
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ Path aliases:
- Forget to provide `TediTranslationService` mock and `TEDI_TRANSLATION_DEFAULT_TOKEN` in tests
- Use `fakeAsync`/`tick` without cleaning up in `afterEach`
- Use `community/` components as reference for coding patterns or style — they are community-contributed and not always reviewed
- Forget to add `.parent__button:hover .tedi-icon { color: inherit; }` (and similar for selected/active states) in components that contain icons and change text color on state — without this, the icon's color modifier class wins over the parent's color. Use `color="inherit"` for component-owned icons; for projected icons, the CSS override ensures the consumer's color applies in default state but inherits on hover/selected
- Style Angular element selectors directly (e.g., `tedi-modal-header { ... }`) — add a CSS class to the host and style the class instead. Exception: third-party elements you can't add classes to (e.g., `cdk-dialog-container`)

## Common Test Setup
Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"ngx-float-ui": "^19.0.1 || ^20.0.0 || ^21.0.0"
},
"dependencies": {
"@tedi-design-system/core": "^6.0.1"
"@tedi-design-system/core": "^6.2.1"
},
"devDependencies": {
"@angular-devkit/core": "19.2.15",
Expand Down
107 changes: 105 additions & 2 deletions skills/tedi-angular/references/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,88 @@ Composed of sub-components:
</tedi-text-group>
```

## Filter

### Filter
**Selector:** `tedi-filter`
**Model:** `selected: boolean`, `value: string`, `values: string[]`
**Inputs:**
- `text: string = ""` — filter label text
- `variant: FilterVariant = "primary"` — "primary" or "secondary"
- `size: FilterSize = "default"` — "default" or "large"
- `multiselect: boolean = false` — multiselect dropdown mode
- `options: FilterOption[] = []` — dropdown options `{ label, value, disabled? }`
- `preserveLabel: boolean = false` — when true, single-select shows "Text: SelectedLabel" instead of replacing text
- `searchable: boolean = false` — show search field in dropdown
- `showSelectAll: boolean = false` — show "Select all" in multiselect
- `showClear: boolean = false` — show clear action in dropdown
- `selectAllLabel: string = "Vali kõik"`
- `clearLabel: string = "Tühjenda valik"`
- `appendTo: string = ""` — append dropdown to selector (e.g., "body")
- `disabled: boolean = false` — also set automatically by a disabled `FormControl` or a disabled parent `FilterGroup`
**Outputs:**
- `cleared: void` — emitted when clear button is clicked in custom content mode
**Slots:**
- `[tediFilterPrepend]` — content before the label (icon, status badge, indicator). Hidden when the filter is selected. In toggle mode (no dropdown), a check icon replaces it; in dropdown modes the prepend is simply removed.
- `[tediFilterContent]` — custom dropdown content (replaces options)

Implements `ControlValueAccessor`. Value type depends on mode: `boolean` (toggle), `string` (single-select), `string[]` (multiselect).

```html
<!-- Boolean toggle -->
<tedi-filter text="Active" variant="secondary" [formControl]="activeControl" />

<!-- Single-select dropdown -->
<tedi-filter text="Service" [options]="options" [(value)]="value" [showClear]="true" appendTo="body" />

<!-- Single-select with label preserved (shows "Service: Option A") -->
<tedi-filter text="Service" [options]="options" [(value)]="value" [preserveLabel]="true" appendTo="body" />

<!-- Multiselect dropdown -->
<tedi-filter text="Hospital" [multiselect]="true" [options]="options" [(values)]="values"
[searchable]="true" [showSelectAll]="true" [showClear]="true" appendTo="body" />

<!-- With prepend content -->
<tedi-filter text="Submitted" variant="secondary" size="large">
<tedi-status-badge tediFilterPrepend text="5" color="brand" />
</tedi-filter>

<!-- Custom dropdown content -->
<tedi-filter [text]="selectedLabel" [selected]="!!selectedValue" [showClear]="true" (cleared)="clear()">
<div tediFilterContent>
<!-- custom content here -->
</div>
</tedi-filter>

<!-- Disabled -->
<tedi-filter text="Service" [options]="options" [(value)]="value" [disabled]="true" />
```

### FilterGroup
**Selector:** `tedi-filter-group`
Wrapper that joins filters into a connected button group with collapsed borders and shared border-radius. Supports `multiselect` and a shared `formControl`/`disabled` state that propagates to children.

```html
<tedi-filter-group>
<tedi-filter text="All" variant="secondary" [selected]="true" />
<tedi-filter text="Active" variant="secondary" />
<tedi-filter text="Closed" variant="secondary" />
</tedi-filter-group>

<!-- Radio-like single-select via shared FormControl -->
<tedi-filter-group label="Type" [formControl]="typeControl">
<tedi-filter text="All" value="all" />
<tedi-filter text="Active" value="active" />
<tedi-filter text="Closed" value="done" />
</tedi-filter-group>

<!-- Multi-select via shared FormControl -->
<tedi-filter-group label="Tags" [multiselect]="true" [formControl]="tagsControl">
<tedi-filter text="Urgent" value="urgent" />
<tedi-filter text="Review" value="review" />
</tedi-filter-group>
```

## Form

### TextField
Expand Down Expand Up @@ -797,14 +879,35 @@ The `[(open)]` binding approach is deprecated. Use `ModalService.open()` for new
- `color: StatusBadgeColor = "neutral"`
- `variant: StatusBadgeVariant = "filled"`
- `size: StatusBadgeSize = "default"`
- `status: StatusBadgeStatus`
- `status: StatusBadgeStatus` — renders a `tedi-status-indicator` in top-right position
- `icon: string = ""`
- `class: string` — custom CSS class
- `title: string` — tooltip/abbreviation title
- `role: string` — ARIA role

```html
<tedi-status-badge text="Active" color="success" status="positive" />
<tedi-status-badge text="Active" color="success" status="success" />
```

### StatusIndicator
**Selector:** `tedi-status-indicator`
**Inputs:**
- `type: StatusIndicatorType = "success"` — "success", "danger", "warning", "inactive"
- `size: StatusIndicatorSize = "sm"` — "sm" or "lg"
- `hasBorder: boolean = false` — white border ring
- `position: StatusIndicatorPosition = "default"` — "default" (inline) or "top-right" (absolute)

Standalone colored dot indicator. Used internally by `StatusBadge` and can be used standalone (e.g., as a prepend in filters).

```html
<tedi-status-indicator type="danger" />
<tedi-status-indicator type="success" size="lg" [hasBorder]="true" />

<!-- Absolute positioned on parent -->
<span style="position: relative">
Lugemata teated
<tedi-status-indicator type="danger" position="top-right" />
</span>
```

---
Expand Down
7 changes: 7 additions & 0 deletions tedi/components/filter/filter-content.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Directive } from "@angular/core";

@Directive({
selector: "[tediFilterContent]",
standalone: true,
})
export class FilterContentDirective {}
45 changes: 45 additions & 0 deletions tedi/components/filter/filter-group.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
.tedi-filter-group {
display: inline-flex;

.tedi-filter {
--_filter-radius: 0;
}

.tedi-filter--secondary+.tedi-filter--secondary {
margin-left: calc(-1 * var(--tedi-borders-01));
}

.tedi-filter--primary:not(:last-child) .tedi-filter__button {
border-right: var(--tedi-borders-01) solid var(--filter-secondary-default-border);

&:hover {
border-right-color: transparent;
}
}

.tedi-filter--primary.tedi-filter--selected:not(:last-child) .tedi-filter__button {
border-right-color: var(--filter-primary-selected-border);
}

.tedi-filter--selected,
.tedi-filter:focus-within {
z-index: 1;
}

.tedi-filter--secondary.tedi-filter--selected+.tedi-filter--secondary,
.tedi-filter--secondary+.tedi-filter--secondary.tedi-filter--selected {
margin-left: calc(-1 * var(--general-selected-border-width));
}

.tedi-filter:first-child {
--_filter-radius: var(--form-checkbox-radio-card-radius) 0 0 var(--form-checkbox-radio-card-radius);
}

.tedi-filter:last-child {
--_filter-radius: 0 var(--form-checkbox-radio-card-radius) var(--form-checkbox-radio-card-radius) 0;
}

.tedi-filter:only-child {
--_filter-radius: var(--form-checkbox-radio-card-radius);
}
}
Loading
Loading