@design-system-rte/angular 0.2.1 → 0.4.1

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 (24) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/package.json +8 -5
  3. package/src/lib/components/button/button.component.scss +11 -13
  4. package/src/lib/components/button/button.component.ts +2 -1
  5. package/src/lib/components/checkbox/checkbox.component.scss +18 -12
  6. package/src/lib/components/checkbox-group/checkbox-group.component.scss +3 -3
  7. package/src/lib/components/icon/icon-map.ts +4 -0
  8. package/src/lib/components/icon/icon.stories.ts +1 -1
  9. package/src/lib/components/icon-button/icon-button.component.html +16 -0
  10. package/src/lib/components/icon-button/icon-button.component.scss +165 -0
  11. package/src/lib/components/icon-button/icon-button.component.ts +40 -0
  12. package/src/lib/components/icon-button/icon-button.stories.ts +200 -0
  13. package/src/lib/components/icon-button-toggle/icon-button-toggle.component.html +12 -0
  14. package/src/lib/components/icon-button-toggle/icon-button-toggle.component.ts +36 -0
  15. package/src/lib/components/icon-button-toggle/icon-button-toggle.stories.ts +197 -0
  16. package/src/lib/components/link/link.component.scss +37 -37
  17. package/src/lib/components/radio-button/radio-button.component.scss +2 -2
  18. package/src/lib/components/radio-button-group/radio-button-group.component.scss +3 -3
  19. package/src/lib/components/split-button/split-button.component.html +56 -0
  20. package/src/lib/components/split-button/split-button.component.scss +288 -0
  21. package/src/lib/components/split-button/split-button.component.stories.ts +227 -0
  22. package/src/lib/components/split-button/split-button.component.ts +55 -0
  23. package/src/lib/components/tooltip/tooltip.component.scss +2 -4
  24. package/tsconfig.lib.json +0 -2
@@ -0,0 +1,12 @@
1
+ <rte-icon-button
2
+ [size]="size()"
3
+ [name]="name()"
4
+ [compactSpacing]="compactSpacing()"
5
+ [disabled]="disabled()"
6
+ [ariaLabel]="ariaLabel()"
7
+ [ariaLabelledBy]="ariaLabelledBy()"
8
+ [type]="type()"
9
+ [variant]="variant()"
10
+ [appearance]="selected() ? 'filled' : 'outlined'"
11
+ (click)="onClick($event)"
12
+ />
@@ -0,0 +1,36 @@
1
+ import { CommonModule } from "@angular/common";
2
+ import { ChangeDetectionStrategy, Component, computed, input, output } from "@angular/core";
3
+ import { ButtonType } from "@design-system-rte/core/components/button/button.interface";
4
+ import { ButtonSize, ButtonVariant } from "@design-system-rte/core/components/button/common/common-button";
5
+ import { buttonIconSize } from "@design-system-rte/core/components/button/common/common-button.constants";
6
+
7
+ import { RegularIconIdKey, TogglableIconIdKey } from "../icon/icon.service";
8
+ import { IconButtonComponent } from "../icon-button/icon-button.component";
9
+
10
+ @Component({
11
+ selector: "rte-icon-button-toggle",
12
+ imports: [CommonModule, IconButtonComponent],
13
+ standalone: true,
14
+ templateUrl: "./icon-button-toggle.component.html",
15
+ changeDetection: ChangeDetectionStrategy.OnPush,
16
+ })
17
+ export class IconButtonToggleComponent {
18
+ readonly disabled = input<boolean>(false);
19
+ readonly name = input.required<RegularIconIdKey | TogglableIconIdKey>();
20
+ readonly size = input<ButtonSize>("m");
21
+ readonly variant = input<ButtonVariant>("primary");
22
+ readonly type = input<ButtonType>("button");
23
+ readonly compactSpacing = input<boolean>(false);
24
+ readonly ariaLabel = input<string | undefined>(undefined);
25
+ readonly ariaLabelledBy = input<string | undefined>(undefined);
26
+ readonly selected = input<boolean>(false);
27
+
28
+ readonly click = output<void>();
29
+
30
+ onClick(event: MouseEvent | KeyboardEvent): void {
31
+ event.stopPropagation();
32
+ this.click.emit();
33
+ }
34
+
35
+ readonly buttonIconSize = computed(() => buttonIconSize[this.size()]);
36
+ }
@@ -0,0 +1,197 @@
1
+ import { ENTER_KEY, SPACE_KEY } from "@design-system-rte/core/constants/keyboard.constants";
2
+ import { Meta, StoryObj } from "@storybook/angular";
3
+ import { fn, userEvent, within, expect } from "@storybook/test";
4
+
5
+ import { togglableIcons as TogglableIconsList } from "../icon/icon-map";
6
+
7
+ import { IconButtonToggleComponent } from "./icon-button-toggle.component";
8
+
9
+ const TogglableIconIds = Object.keys(TogglableIconsList);
10
+
11
+ const meta = {
12
+ title: "IconButtonToggle",
13
+ component: IconButtonToggleComponent,
14
+ tags: ["autodocs"],
15
+ argTypes: {
16
+ name: {
17
+ control: "select",
18
+ options: [...TogglableIconIds].sort((a, b) => a.localeCompare(b)),
19
+ description: "Nom de l’icône à afficher",
20
+ defaultValue: "check",
21
+ },
22
+ variant: {
23
+ control: "select",
24
+ options: ["primary", "secondary", "text", "transparent", "danger"],
25
+ },
26
+ size: {
27
+ control: "select",
28
+ options: ["s", "m", "l"],
29
+ },
30
+ compactSpacing: {
31
+ control: "boolean",
32
+ description: "Utiliser un espacement compact",
33
+ },
34
+ disabled: {
35
+ control: "boolean",
36
+ },
37
+ selected: {
38
+ control: "boolean",
39
+ description: "Indique si l'icône est sélectionnée",
40
+ },
41
+ },
42
+ } satisfies Meta<IconButtonToggleComponent>;
43
+
44
+ export default meta;
45
+
46
+ type Story = StoryObj<IconButtonToggleComponent>;
47
+
48
+ const mockFn = fn();
49
+
50
+ export const Default: Story = {
51
+ args: {
52
+ name: "settings",
53
+ size: "m",
54
+ disabled: false,
55
+ compactSpacing: false,
56
+ variant: "primary",
57
+ ariaLabel: "icon button aria label",
58
+ click: mockFn,
59
+ selected: false,
60
+ },
61
+
62
+ play: async ({ canvasElement }) => {
63
+ const canvas = within(canvasElement);
64
+ const iconButton = canvas.getByRole("button");
65
+ await userEvent.click(iconButton);
66
+ expect(mockFn).toHaveBeenCalled();
67
+ iconButton.blur();
68
+ },
69
+ };
70
+
71
+ export const Sizing: Story = {
72
+ args: {
73
+ ...Default.args,
74
+ compactSpacing: false,
75
+ },
76
+ render: (args) => ({
77
+ props: { ...args },
78
+ template: `
79
+ <div style="display: flex; gap: 8px">
80
+ <rte-icon-button-toggle
81
+ size="s"
82
+ name=${args.name}
83
+ data-testid="small-icon-button"
84
+ [compactSpacing]="${args.compactSpacing}"
85
+ [disabled]="${args.disabled}"
86
+ [selected]="${args.selected}"
87
+ [ariaLabel]="'Small Icon Button'"
88
+ [type]="'${args.type}'"
89
+ [variant]="'${args.variant}'"
90
+ />
91
+ <rte-icon-button-toggle
92
+ name=${args.name}
93
+ data-testid="medium-icon-button"
94
+ [compactSpacing]="${args.compactSpacing}"
95
+ [disabled]="${args.disabled}"
96
+ [selected]="${args.selected}"
97
+ [ariaLabel]="'Small Icon Button'"
98
+ [type]="'${args.type}'"
99
+ [variant]="'${args.variant}'"
100
+ />
101
+ <rte-icon-button-toggle
102
+ size="l"
103
+ name=${args.name}
104
+ data-testid="large-icon-button"
105
+ [compactSpacing]="${args.compactSpacing}"
106
+ [disabled]="${args.disabled}"
107
+ [selected]="${args.selected}"
108
+ [ariaLabel]="'Small Icon Button'"
109
+ [type]="'${args.type}'"
110
+ [variant]="'${args.variant}'"
111
+ />
112
+ </div>
113
+ `,
114
+ }),
115
+ play: async ({ canvasElement }) => {
116
+ const canvas = within(canvasElement);
117
+ const smallIconButton = canvas.getByTestId("small-icon-button").getElementsByTagName("button")[0];
118
+ const mediumIconButton = canvas.getByTestId("medium-icon-button").getElementsByTagName("button")[0];
119
+ const largeIconButton = canvas.getByTestId("large-icon-button").getElementsByTagName("button")[0];
120
+
121
+ expect(smallIconButton.clientHeight).toBe(24);
122
+ expect(mediumIconButton.clientHeight).toBe(32);
123
+ expect(largeIconButton.clientHeight).toBe(40);
124
+ },
125
+ };
126
+
127
+ export const CompactSizing: Story = {
128
+ args: {
129
+ ...Default.args,
130
+ compactSpacing: true,
131
+ },
132
+ render: (args) => ({
133
+ props: { ...args },
134
+ template: `
135
+ <div style="display: flex; gap: 8px">
136
+ <rte-icon-button-toggle
137
+ size="s"
138
+ name=${args.name}
139
+ data-testid="small-icon-button"
140
+ [compactSpacing]="${args.compactSpacing}"
141
+ [disabled]="${args.disabled}"
142
+ [selected]="${args.selected}"
143
+ [ariaLabel]="'Small Icon Button'"
144
+ [type]="'${args.type}'"
145
+ [variant]="'${args.variant}'"
146
+ />
147
+ <rte-icon-button-toggle
148
+ name=${args.name}
149
+ data-testid="medium-icon-button"
150
+ [compactSpacing]="${args.compactSpacing}"
151
+ [disabled]="${args.disabled}"
152
+ [selected]="${args.selected}"
153
+ [ariaLabel]="'Small Icon Button'"
154
+ [type]="'${args.type}'"
155
+ [variant]="'${args.variant}'"
156
+ />
157
+ <rte-icon-button-toggle
158
+ size="l"
159
+ name=${args.name}
160
+ data-testid="large-icon-button"
161
+ [compactSpacing]="${args.compactSpacing}"
162
+ [disabled]="${args.disabled}"
163
+ [selected]="${args.selected}"
164
+ [ariaLabel]="'Small Icon Button'"
165
+ [type]="'${args.type}'"
166
+ [variant]="'${args.variant}'"
167
+ />
168
+ </div>
169
+ `,
170
+ }),
171
+ play: async ({ canvasElement }) => {
172
+ const canvas = within(canvasElement);
173
+ const smallIconButton = canvas.getByTestId("small-icon-button").getElementsByTagName("button")[0];
174
+ const mediumIconButton = canvas.getByTestId("medium-icon-button").getElementsByTagName("button")[0];
175
+ const largeIconButton = canvas.getByTestId("large-icon-button").getElementsByTagName("button")[0];
176
+
177
+ expect(smallIconButton.clientHeight).toBe(16);
178
+ expect(mediumIconButton.clientHeight).toBe(20);
179
+ expect(largeIconButton.clientHeight).toBe(24);
180
+ },
181
+ };
182
+
183
+ export const KeyboardInteraction: Story = {
184
+ args: {
185
+ ...Default.args,
186
+ },
187
+ play: async ({ canvasElement }) => {
188
+ const canvas = within(canvasElement);
189
+ const button = canvas.getByRole("button");
190
+ await userEvent.tab();
191
+ expect(button).toHaveFocus();
192
+ await userEvent.keyboard(ENTER_KEY);
193
+ await userEvent.keyboard(SPACE_KEY);
194
+ expect(mockFn).toHaveBeenCalledTimes(2);
195
+ button.blur();
196
+ },
197
+ };
@@ -1,4 +1,4 @@
1
- @use '@design-system-rte/core/tokens/main.scss' as *;
1
+ @use '@design-system-rte/core/design-tokens/main.scss' as *;
2
2
 
3
3
  .rte-link {
4
4
 
@@ -8,54 +8,54 @@
8
8
  display: inline-flex;
9
9
  justify-content: center;
10
10
 
11
- &:visited {
11
+ &:visited {
12
12
 
13
- color: var(--content-link-visited);
14
- text-decoration: underline;
13
+ color: var(--content-link-visited);
14
+ text-decoration: underline;
15
15
 
16
- &:hover {
17
- color: var(--content-link-visited-hover);
18
- text-decoration: none;
19
- }
16
+ &:hover {
17
+ color: var(--content-link-visited-hover);
18
+ text-decoration: none;
19
+ }
20
20
 
21
- &:active {
22
- color: var(--content-link-visited-press);
23
- text-decoration: underline;
24
- }
21
+ &:active {
22
+ color: var(--content-link-visited-press);
23
+ text-decoration: underline;
24
+ }
25
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
- }
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;
33
32
  }
33
+ }
34
34
 
35
- &:not(:visited) {
35
+ &:not(:visited) {
36
36
 
37
- color: var(--content-link-default);
37
+ color: var(--content-link-default);
38
38
 
39
- &:hover {
40
- color: var(--content-link-hover);
41
- text-decoration: none;
42
- }
39
+ &:hover {
40
+ color: var(--content-link-hover);
41
+ text-decoration: none;
42
+ }
43
43
 
44
- &:active {
45
- color: var(--content-link-press);
46
- text-decoration: underline;
47
- }
44
+ &:active {
45
+ color: var(--content-link-press);
46
+ text-decoration: underline;
47
+ }
48
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
- }
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;
56
55
  }
56
+ }
57
57
 
58
- &.subtle{
58
+ &.subtle {
59
59
 
60
60
  &:visited {
61
61
 
@@ -1,4 +1,4 @@
1
- @use '@design-system-rte/core/tokens/main.scss' as *;
1
+ @use '@design-system-rte/core/design-tokens/main.scss' as *;
2
2
 
3
3
 
4
4
  .rte-radio-button-container {
@@ -78,7 +78,7 @@
78
78
  border: $width-xs solid var(--content-tertiary);
79
79
  }
80
80
 
81
- &.error{
81
+ &.error {
82
82
  border: $width-xs solid var(--content-danger);
83
83
  }
84
84
  }
@@ -1,4 +1,4 @@
1
- @use '@design-system-rte/core/tokens/main.scss' as *;
1
+ @use '@design-system-rte/core/design-tokens/main.scss' as *;
2
2
 
3
3
  .radio-button-group-container {
4
4
  display: flex;
@@ -31,7 +31,7 @@
31
31
  margin: $positive-spacing_0;
32
32
  margin-top: $positive-spacing_050;
33
33
  }
34
-
34
+
35
35
  &.error {
36
36
  .group-title {
37
37
  color: var(--content-danger);
@@ -43,7 +43,7 @@
43
43
  color: var(--content-tertiary);
44
44
  }
45
45
 
46
- .error{
46
+ .error {
47
47
  .group-title {
48
48
  color: var(--content-danger);
49
49
  }
@@ -0,0 +1,56 @@
1
+ <div
2
+ class="split-button-container size-{{ size() }} {{appearance()}}"
3
+ [ngClass]="{'compact-spacing': compactSpacing()}"
4
+ >
5
+ <button
6
+ class="split-button-left size-{{ size() }}"
7
+ data-testid="Main action button"
8
+ type="button"
9
+ [disabled]="disabled()"
10
+ >
11
+ <rte-icon
12
+ *ngIf="icon()"
13
+ [name]="icon()!"
14
+ [size]="splitButtonLeftIconSize()"
15
+ />
16
+ <p class="split-button-label size-{{ size() }}">
17
+ {{label()}}
18
+ </p>
19
+ </button>
20
+
21
+ <div
22
+ class="split-button-divider"
23
+ [ngClass]="{'disabled': disabled()}"
24
+ ></div>
25
+
26
+ <div class="split-button-right-container">
27
+ <button
28
+ class="split-button-right size-{{ size() }}"
29
+ data-testid="Menu button"
30
+ type="button"
31
+ aria-haspopup="menu"
32
+ [attr.aria-expanded]="isOpen()"
33
+ [attr.aria-label]="ariaLabelRight()"
34
+ [attr.data-selected]="selected()"
35
+ [disabled]="disabled()"
36
+ (click)="isOpen.set(true)"
37
+ (mouseenter)="isOpen.set(true)"
38
+ (mouseleave)="isOpen.set(false)"
39
+ (keydown)="handleKeyDownOnRightButton($event)"
40
+ >
41
+ <div class="split-button-right-icon-container">
42
+ <rte-icon name="arrow-chevron-down"[size]="splitButtonRightIconSize()"/>
43
+ </div>
44
+ </button>
45
+
46
+ <div
47
+ class="split-button-dropdown position-{{ position() }}"
48
+ role="menu"
49
+ data-testid="Menu container"
50
+ [style.visibility]="isOpen() ? 'visible' : 'hidden'"
51
+ (keydown)="handleKeyDownOnMenu($event)"
52
+ >
53
+ <ng-content/>
54
+ </div>
55
+ </div>
56
+ </div>