@design-system-rte/angular 0.1.1-rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/.editorconfig +15 -0
  2. package/.storybook/main.ts +25 -0
  3. package/.storybook/preview.scss +10 -0
  4. package/.storybook/preview.ts +27 -0
  5. package/.storybook/tsconfig.doc.json +7 -0
  6. package/.storybook/tsconfig.json +11 -0
  7. package/.storybook/typings.d.ts +4 -0
  8. package/.vscode/extensions.json +4 -0
  9. package/.vscode/launch.json +20 -0
  10. package/.vscode/tasks.json +42 -0
  11. package/angular.json +127 -0
  12. package/package.json +55 -0
  13. package/src/assets/.gitkeep +0 -0
  14. package/src/components/button/button.component.html +7 -0
  15. package/src/components/button/button.component.scss +156 -0
  16. package/src/components/button/button.component.spec.ts +23 -0
  17. package/src/components/button/button.component.stories.ts +103 -0
  18. package/src/components/button/button.component.ts +25 -0
  19. package/src/components/grid/col/col.directive.ts +37 -0
  20. package/src/components/grid/grid.directive.stories.ts +149 -0
  21. package/src/components/grid/grid.directive.ts +24 -0
  22. package/src/components/link/link.component.html +5 -0
  23. package/src/components/link/link.component.scss +108 -0
  24. package/src/components/link/link.component.stories.ts +61 -0
  25. package/src/components/link/link.component.ts +19 -0
  26. package/src/components/radio-button/radio-button.component.html +24 -0
  27. package/src/components/radio-button/radio-button.component.scss +140 -0
  28. package/src/components/radio-button/radio-button.component.stories.ts +75 -0
  29. package/src/components/radio-button/radio-button.component.ts +23 -0
  30. package/src/components/radio-button-group/radio-button-group.component.html +45 -0
  31. package/src/components/radio-button-group/radio-button-group.component.scss +82 -0
  32. package/src/components/radio-button-group/radio-button-group.component.stories.ts +124 -0
  33. package/src/components/radio-button-group/radio-button-group.component.ts +30 -0
  34. package/tsconfig.app.json +14 -0
  35. package/tsconfig.json +32 -0
  36. package/tsconfig.spec.json +14 -0
@@ -0,0 +1,37 @@
1
+ import { Directive, HostBinding, input } from '@angular/core';
2
+
3
+ @Directive({
4
+ selector: '[dsCol]',
5
+ standalone: true
6
+ })
7
+ export class ColDirective {
8
+
9
+ xxs = input<number>()
10
+ xs = input<number>()
11
+ s = input<number>()
12
+ m = input<number>()
13
+ l = input<number>()
14
+ xl = input<number>()
15
+
16
+ @HostBinding ("class")
17
+ get colClasses(): string {
18
+ return [
19
+ 'col',
20
+ this.generateColumnClass('col-xxs', this.xxs()),
21
+ this.generateColumnClass('col-xs', this.xs()),
22
+ this.generateColumnClass('col-s', this.s()),
23
+ this.generateColumnClass('col-m', this.m()),
24
+ this.generateColumnClass('col-l', this.l()),
25
+ this.generateColumnClass('col-xl', this.xl()),
26
+ ]
27
+ .filter(Boolean)
28
+ .join(' ');
29
+ }
30
+
31
+ constructor() { }
32
+
33
+ private generateColumnClass(prefix: string, size?: number): string {
34
+ return size ? `${prefix}-${size}` : '';
35
+ }
36
+
37
+ }
@@ -0,0 +1,149 @@
1
+ import { CommonModule } from '@angular/common';
2
+ import { GridType } from '@design-system-rte/core/components/grid/grid.interface';
3
+ import { componentWrapperDecorator, moduleMetadata, type Meta, type StoryObj } from '@storybook/angular';
4
+ import { GridDirective } from './grid.directive';
5
+ import { ColDirective } from './col/col.directive';
6
+
7
+ type GridStoriesArgs = GridDirective;
8
+
9
+ const COLUMN_NUMBER = 12;
10
+
11
+ const meta: Meta<GridStoriesArgs>= {
12
+ title: 'Grid',
13
+ component: GridDirective,
14
+ tags: ['autodocs'],
15
+ argTypes: {
16
+ gridType: {
17
+ control: 'select',
18
+ defaultValue: (args: GridStoriesArgs) => args.gridType,
19
+ options: ['fluid', 'fixed-narrow', 'fixed-wide'],
20
+ description: 'The type of grid to use',
21
+ },
22
+ },
23
+ decorators: [
24
+ moduleMetadata({
25
+ imports: [CommonModule, ColDirective],
26
+ }),
27
+ componentWrapperDecorator((story) => `<div class="sb-css-grid-container">${story}</div>`)
28
+ ],
29
+ };
30
+
31
+ export default meta;
32
+ type Story = StoryObj<GridStoriesArgs>;
33
+
34
+ const defaultTemplate = (gridType: GridType) => {
35
+ return `
36
+ <div rte-grid
37
+ [gridType]="'${gridType}'"
38
+ data-testid="grid"
39
+ >
40
+ <ng-container *ngFor="let item of items">
41
+ <div
42
+ dsCol
43
+ ></div>
44
+ </ng-container>
45
+ </div>
46
+ `;
47
+ };
48
+
49
+ export const Fluid: Story = {
50
+ args: {
51
+ gridType: 'fluid',
52
+ },
53
+ render: (args) => ({
54
+ props: {
55
+ ...args,
56
+ items: Array.from(Array(COLUMN_NUMBER)).map((_,i)=>i+1)
57
+ },
58
+ template: defaultTemplate(args.gridType),
59
+ }),
60
+ };
61
+
62
+ export const FixedWide: Story = {
63
+ args: {
64
+ gridType: 'fixed-wide',
65
+ },
66
+ render: (args) => ({
67
+ props: {
68
+ ...args,
69
+ items: Array.from(Array(COLUMN_NUMBER)).map((_,i)=>i+1)
70
+ },
71
+ template: defaultTemplate(args.gridType),
72
+ }),
73
+ };
74
+
75
+ export const FixedNarrow: Story = {
76
+ args: {
77
+ gridType: 'fixed-narrow',
78
+ },
79
+ render: (args) => ({
80
+ props: {
81
+ ...args,
82
+ items: Array.from(Array(COLUMN_NUMBER)).map((_,i)=>i+1)
83
+ },
84
+ template: defaultTemplate(args.gridType),
85
+ }),
86
+ };
87
+
88
+ export const ResponsiveColumns: Story = {
89
+ args: {
90
+ gridType: 'fluid',
91
+ },
92
+ render: (args) => ({
93
+ template:`
94
+ <div rte-grid
95
+ [gridType]="'${args.gridType}'"
96
+ data-testid="grid"
97
+ >
98
+ <div dsCol [xxs]=1 [xs]=1 [s]=3 [m]=4 [l]=4 [xl]=12>
99
+ <div>
100
+ <p>xxs : Span 1 de 2</p>
101
+ <p>xs : Span 1 de 6</p>
102
+ <p>s : Span 3 de 6</p>
103
+ </div>
104
+ <div>
105
+ <p>m : Span 4 de 12</p>
106
+ <p>l : Span 4 de 12</p>
107
+ <p>xl : Span 12 de 12</p>
108
+ </div>
109
+ </div>
110
+ <div dsCol [xxs]=1 [xs]=3 [s]=3 [m]=4 [l]=8 [xl]=12>
111
+ <div>
112
+ <p>xxs : Span 1 de 2</p>
113
+ <p>xs : Span 3 de 6</p>
114
+ <p>s : Span 3 de 6</p>
115
+ </div>
116
+ <div>
117
+ <p>m : Span 4 de 12</p>
118
+ <p>l : Span 8 de 12</p>
119
+ <p>xl : Span 12 de 12</p>
120
+ </div>
121
+ </div>
122
+ <div dsCol [xxs]=2 [xs]=2 [s]=3 [m]=4 [l]=10 [xl]=12>
123
+ <div>
124
+ <p>xxs : Span 2 de 2</p>
125
+ <p>xs : Span 2 de 6</p>
126
+ <p>s : Span 3 de 6</p>
127
+ </div>
128
+ <div>
129
+ <p>m : Span 4 de 12</p>
130
+ <p>l : Span 10 de 12</p>
131
+ <p>xl : Span 12 de 12</p>
132
+ </div>
133
+ </div>
134
+ <div dsCol [xxs]=2 [xs]=6 [s]=6 [m]=4 [l]=12 [xl]=12>
135
+ <div>
136
+ <p>xxs : Span 2 de 2</p>
137
+ <p>xs : Span 6 de 6</p>
138
+ <p>s : Span 6 de 6</p>
139
+ </div>
140
+ <div>
141
+ <p>m : Span 4 de 12</p>
142
+ <p>l : Span 12 de 12</p>
143
+ <p>xl : Span 12 de 12</p>
144
+ </div>
145
+ </div>
146
+ </div>
147
+ `
148
+ }),
149
+ };
@@ -0,0 +1,24 @@
1
+ import { Directive, HostBinding, input } from '@angular/core';
2
+ import { GridType } from '@design-system-rte/core/components/grid/grid.interface';
3
+
4
+ @Directive({
5
+ selector: '[rte-grid]',
6
+ standalone: true
7
+ })
8
+ export class GridDirective {
9
+
10
+ gridType = input<GridType>('fluid');
11
+
12
+ @HostBinding("class")
13
+ get hostClasses(): string {
14
+ return 'grid'
15
+ }
16
+
17
+ @HostBinding("attr.data-gridtype")
18
+ get hostDataClasses(): string {
19
+ const variation = this.gridType();
20
+ return `${variation}`;
21
+ }
22
+ constructor() { }
23
+
24
+ }
@@ -0,0 +1,5 @@
1
+ <a class="rte-link" [ngClass]="{'subtle': subtle()}" href="{{ href() }}" role="link">
2
+ <span class="rte-link-label">
3
+ {{ label() }}
4
+ </span>
5
+ </a>
@@ -0,0 +1,108 @@
1
+ @use '@design-system-rte/core/tokens/main.scss' as *;
2
+
3
+ .rte-link {
4
+
5
+ @include typography-link-m;
6
+ align-items: center;
7
+ cursor: pointer;
8
+ display: inline-flex;
9
+ justify-content: center;
10
+
11
+ &:visited {
12
+
13
+ color: var(--content-link-visited);
14
+ text-decoration: underline;
15
+
16
+ &:hover {
17
+ color: var(--content-link-visited-hover);
18
+ text-decoration: none;
19
+ }
20
+
21
+ &:active {
22
+ color: var(--content-link-visited-press);
23
+ text-decoration: underline;
24
+ }
25
+
26
+ &:focus-visible {
27
+ color: var(--content-link-visited);
28
+ text-decoration: underline;
29
+ outline: 1px solid var(--border-brand-focused);
30
+ outline-offset: $radius-s;
31
+ border-radius: $radius-s;
32
+ }
33
+ }
34
+
35
+ &:not(:visited) {
36
+
37
+ color: var(--content-link-default);
38
+
39
+ &:hover {
40
+ color: var(--content-link-hover);
41
+ text-decoration: none;
42
+ }
43
+
44
+ &:active {
45
+ color: var(--content-link-press);
46
+ text-decoration: underline;
47
+ }
48
+
49
+ &:focus-visible {
50
+ color: var(--content-link-default);
51
+ text-decoration: underline;
52
+ outline: 1px solid var(--border-brand-focused);
53
+ outline-offset: $radius-s;
54
+ border-radius: $radius-s;
55
+ }
56
+ }
57
+
58
+ &.subtle{
59
+
60
+ &:visited {
61
+
62
+ color: var(--content-primary);
63
+ text-decoration: none;
64
+
65
+ &:hover {
66
+ color: var(--content-link-secondary);
67
+ text-decoration: underline;
68
+ }
69
+
70
+ &:active {
71
+ color: var(--content-link-primary);
72
+ text-decoration: none;
73
+ }
74
+
75
+ &:focus-visible {
76
+ color: var(--content-link-primary);
77
+ text-decoration: none;
78
+ outline: 1px solid var(--border-brand-focused);
79
+ outline-offset: $radius-s;
80
+ border-radius: $radius-s;
81
+ }
82
+ }
83
+
84
+ &:not(:visited) {
85
+
86
+ color: var(--content-primary);
87
+ text-decoration: none;
88
+
89
+ &:hover {
90
+ color: var(--content-secondary);
91
+ text-decoration: underline;
92
+ }
93
+
94
+ &:active {
95
+ color: var(--content-primary);
96
+ text-decoration: none;
97
+ }
98
+
99
+ &:focus-visible {
100
+ color: var(--content-primary);
101
+ text-decoration: none;
102
+ outline: 1px solid var(--border-brand-focused);
103
+ outline-offset: $radius-s;
104
+ border-radius: $radius-s;
105
+ }
106
+ }
107
+ }
108
+ }
@@ -0,0 +1,61 @@
1
+ import { Meta, StoryObj } from '@storybook/angular';
2
+ import { fn, userEvent, within, expect } from '@storybook/test';
3
+
4
+ import { LinkComponent } from './link.component';
5
+
6
+ const meta: Meta<LinkComponent> = {
7
+ title: 'Link',
8
+ component: LinkComponent,
9
+ tags: ['autodocs'],
10
+ argTypes: {
11
+ subtle: {
12
+ control: 'boolean',
13
+ },
14
+ externalLink: {
15
+ control: 'boolean',
16
+ },
17
+ },
18
+ };
19
+ export default meta;
20
+ type Story = StoryObj<LinkComponent>;
21
+
22
+ export const Default: Story = {
23
+ args: {
24
+ label: 'Link',
25
+ href: '#',
26
+ },
27
+ };
28
+
29
+ export const SubtleLink: Story = {
30
+ args: {
31
+ ...Default.args,
32
+ subtle: true,
33
+ },
34
+ }
35
+
36
+ export const SubtleLinkExternal: Story = {
37
+ args: {
38
+ ...SubtleLink.args,
39
+ externalLink: true,
40
+ },
41
+ }
42
+
43
+ export const ExternalLink: Story = {
44
+ args: {
45
+ ...Default.args,
46
+ externalLink: true,
47
+ },
48
+ };
49
+
50
+ export const KeyboardInteraction: Story = {
51
+ args: {
52
+ ...Default.args,
53
+ href: '#',
54
+ },
55
+ play: async ({ canvasElement }) => {
56
+ const canvas = within(canvasElement);
57
+ const link = canvas.getByRole('link');
58
+ await userEvent.tab();
59
+ expect(link).toHaveFocus();
60
+ }
61
+ };
@@ -0,0 +1,19 @@
1
+ import { Component, input } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+
4
+ @Component({
5
+ selector: 'rte-link',
6
+ standalone: true,
7
+ imports: [CommonModule],
8
+ templateUrl: './link.component.html',
9
+ styleUrl: './link.component.scss'
10
+ })
11
+
12
+ export class LinkComponent {
13
+
14
+ label = input('');
15
+ href = input<string>('#');
16
+ subtle = input<boolean>(false);
17
+ externalLink = input<boolean>(false);
18
+
19
+ }
@@ -0,0 +1,24 @@
1
+ <div *ngIf="isDisplayed()" class="rte-radio-button-container">
2
+ <input
3
+ type="radio"
4
+ [id]="label()"
5
+ [value]="label()"
6
+ [size]="labelSize"
7
+ [name]="groupName()"
8
+ class="rte-radio-button"
9
+ [ngClass]="{'error': error(), 'read-only': readOnly()}"
10
+ [disabled]="disabled()"
11
+ />
12
+ <label
13
+ *ngIf="showLabel()"
14
+ [for]="label()"
15
+ class="rte-radio-button-label"
16
+ [ngClass]="{
17
+ 'error': error(),
18
+ 'read-only': readOnly(),
19
+ 'disabled': disabled()
20
+ }"
21
+ >
22
+ {{ label() }}
23
+ </label>
24
+ </div>
@@ -0,0 +1,140 @@
1
+ @use '@design-system-rte/core/tokens/main.scss' as *;
2
+
3
+
4
+ .rte-radio-button-container {
5
+ display: flex;
6
+ padding-right: $positive-spacing_050;
7
+ align-items: center;
8
+ gap: $positive-spacing_150;
9
+ }
10
+
11
+ .rte-radio-button {
12
+ appearance: none;
13
+ display: flex;
14
+ width: 16px;
15
+ height: 16px;
16
+ padding: $positive-spacing_0;
17
+ justify-content: center;
18
+ align-items: center;
19
+ border-radius: $radius-pill;
20
+ border: $width-xs solid var(--content-tertiary);
21
+ background: var(--background-default);
22
+ position: relative;
23
+ cursor: pointer;
24
+ transition: box-shadow 0.3s ease-in-out;
25
+ margin: 0;
26
+
27
+ &::before {
28
+ content: '';
29
+ width: 10px;
30
+ height: 10px;
31
+ border-radius: $radius-pill;
32
+ background: var(--content-brand-default);
33
+ opacity: $opacity-0;
34
+ }
35
+
36
+ &::after {
37
+ content: '';
38
+ width: calc(100% + 8px);
39
+ height: calc(100% + 8px);
40
+ border-radius: $radius-s;
41
+ border: $width-xs solid var(--content-primary);
42
+ position: absolute;
43
+ z-index: -1;
44
+ opacity: $opacity-0;
45
+ }
46
+
47
+ &.error {
48
+ border: $width-xs solid var(--content-danger);
49
+ transition: box-shadow 0.3s ease-in-out;
50
+
51
+ &:checked {
52
+ border: $width-xs solid var(--content-danger);
53
+ }
54
+
55
+ &::before {
56
+ background: var(--content-danger);
57
+ }
58
+
59
+ //TODO: Modify brute variable to use tokens
60
+
61
+ &:hover {
62
+ transition: box-shadow 0.15s ease-in-out;
63
+ box-shadow: 0 0 0 8px rgba(200, 22, 64, 0.2);
64
+
65
+ &:checked {
66
+ box-shadow: 0 0 0 8px rgba(200, 22, 64, 0.2);
67
+ }
68
+ }
69
+ }
70
+
71
+ &.read-only {
72
+ pointer-events: none;
73
+ cursor: default;
74
+
75
+ &::before {
76
+ background: var(--content-tertiary);
77
+ }
78
+
79
+ &:checked {
80
+ border: $width-xs solid var(--content-tertiary);
81
+ }
82
+
83
+ &.error{
84
+ border: $width-xs solid var(--content-danger);
85
+ }
86
+ }
87
+
88
+ &:focus {
89
+ outline: none;
90
+
91
+ &::after {
92
+ opacity: $opacity-100;
93
+ }
94
+ }
95
+
96
+ &:checked {
97
+ border: $width-xs solid var(--content-brand-default);
98
+ }
99
+
100
+ &:checked::before {
101
+ opacity: $opacity-100;
102
+ }
103
+
104
+ //TODO: Modify brute variable to use tokens
105
+
106
+ &:hover {
107
+ transition: box-shadow 0.15s ease-in-out;
108
+ box-shadow: 0 0 0 8px var(--background-hover);
109
+
110
+ &:checked {
111
+ box-shadow: 0 0 0 8px rgba(34, 80, 130, 0.2);
112
+ }
113
+ }
114
+
115
+ &:disabled {
116
+ pointer-events: none;
117
+ background: var(--background-disabled);
118
+ border: $width-xs solid var(--content-disabled);
119
+ cursor: not-allowed;
120
+
121
+ &::before {
122
+ background: var(--content-disabled);
123
+ }
124
+ }
125
+ }
126
+
127
+ .rte-radio-button-label {
128
+ @include typography-radio-button-l;
129
+
130
+ &.read-only {
131
+ pointer-events: none;
132
+ cursor: default;
133
+ color: var(--content-tertiary);
134
+ }
135
+
136
+ &.disabled {
137
+ pointer-events: none;
138
+ color: var(--content-disabled);
139
+ }
140
+ }
@@ -0,0 +1,75 @@
1
+ import { Meta, StoryObj } from '@storybook/angular';
2
+ import { userEvent, within, expect } from '@storybook/test';
3
+ import { RadioButtonComponent } from './radio-button.component';
4
+
5
+ const meta: Meta<RadioButtonComponent> = {
6
+ title: 'RadioButton',
7
+ component: RadioButtonComponent,
8
+ tags: ['autodocs'],
9
+ argTypes: {
10
+ label: {
11
+ control: 'text',
12
+ defaultValue: 'Radio Button',
13
+ },
14
+ groupName: {
15
+ control: 'text',
16
+ defaultValue: 'radio-group',
17
+ },
18
+ showLabel: {
19
+ control: 'boolean',
20
+ defaultValue: true,
21
+ },
22
+ disabled: {
23
+ control: 'boolean',
24
+ defaultValue: false,
25
+ },
26
+ error: {
27
+ control: 'boolean',
28
+ defaultValue: false,
29
+ },
30
+ readOnly: {
31
+ control: 'boolean',
32
+ defaultValue: false,
33
+ },
34
+ },
35
+ };
36
+ export default meta;
37
+ type Story = StoryObj<RadioButtonComponent>;
38
+
39
+ export const Default: Story = {
40
+ args: {
41
+ label: 'Radio Button',
42
+ groupName: 'radio-group',
43
+ showLabel: true,
44
+ disabled: false,
45
+ error: false,
46
+ readOnly: false,
47
+ },
48
+ play: async ({ canvasElement }) => {
49
+ const canvas = within(canvasElement);
50
+ const radioButton = canvas.getByRole('radio');
51
+ await userEvent.click(radioButton);
52
+ expect(radioButton).toBeChecked();
53
+ },
54
+ };
55
+
56
+ export const Disabled: Story = {
57
+ args: {
58
+ ...Default.args,
59
+ disabled: true,
60
+ },
61
+ }
62
+
63
+ export const Error: Story = {
64
+ args: {
65
+ ...Default.args,
66
+ error: true,
67
+ },
68
+ }
69
+
70
+ export const ReadOnly: Story = {
71
+ args: {
72
+ ...Default.args,
73
+ readOnly: true,
74
+ },
75
+ }
@@ -0,0 +1,23 @@
1
+ import { Component,computed,input } from "@angular/core";
2
+ import { labelSize } from "@design-system-rte/core/components/radio-button/radio-button.constants";
3
+ import { CommonModule } from "@angular/common";
4
+
5
+ @Component({
6
+ selector: 'rte-radio-button',
7
+ standalone: true,
8
+ imports: [CommonModule],
9
+ templateUrl: './radio-button.component.html',
10
+ styleUrl: './radio-button.component.scss',
11
+ })
12
+
13
+ export class RadioButtonComponent {
14
+ label = input('');
15
+ groupName = input('');
16
+ showLabel = input(true);
17
+ disabled = input(false);
18
+ error = input(false);
19
+ readOnly = input(false);
20
+ labelSize = labelSize;
21
+
22
+ isDisplayed = computed(() => !(this.disabled() && this.error()));
23
+ }